module Eventful.Projection
( Projection (..)
, latestProjection
, allProjections
, StreamProjection (..)
, VersionedStreamProjection
, GlobalStreamProjection
, streamProjection
, versionedStreamProjection
, globalStreamProjection
, getLatestStreamProjection
, serializedProjection
, projectionMapMaybe
) where
import Data.Foldable (foldl')
import Data.Functor.Contravariant
import Data.List (scanl')
import Eventful.Serializer
import Eventful.Store.Class
import Eventful.UUID
data Projection state event
= Projection
{ projectionSeed :: state
, projectionEventHandler :: state -> event -> state
}
instance Contravariant (Projection state) where
contramap f (Projection seed handler) = Projection seed handler'
where
handler' state event = handler state (f event)
latestProjection :: (Foldable t) => Projection state event -> t event -> state
latestProjection (Projection seed handler) = foldl' handler seed
allProjections :: Projection state event -> [event] -> [state]
allProjections (Projection seed handler) = scanl' handler seed
data StreamProjection key position state event
= StreamProjection
{ streamProjectionKey :: !key
, streamProjectionPosition :: !position
, streamProjectionProjection :: !(Projection state event)
, streamProjectionState :: !state
}
type VersionedStreamProjection = StreamProjection UUID EventVersion
type GlobalStreamProjection state event = StreamProjection () SequenceNumber state (VersionedStreamEvent event)
streamProjection
:: key
-> position
-> Projection state event
-> StreamProjection key position state event
streamProjection key position projection@Projection{..} =
StreamProjection key position projection projectionSeed
versionedStreamProjection
:: UUID
-> Projection state event
-> VersionedStreamProjection state event
versionedStreamProjection uuid = streamProjection uuid (1)
globalStreamProjection
:: Projection state (VersionedStreamEvent event)
-> GlobalStreamProjection state event
globalStreamProjection = streamProjection () 0
streamProjectionEventHandler
:: StreamProjection key position state event
-> StreamEvent eventKey position event
-> StreamProjection key position state event
streamProjectionEventHandler StreamProjection{..} event =
let
Projection{..} = streamProjectionProjection
position' = streamEventPosition event
state' = projectionEventHandler streamProjectionState (streamEventEvent event)
in StreamProjection streamProjectionKey position' streamProjectionProjection state'
getLatestStreamProjection
:: (Monad m, Num position)
=> EventStoreReader key position m (StreamEvent key position event)
-> StreamProjection key position state event
-> m (StreamProjection key position state event)
getLatestStreamProjection (EventStoreReader getEvents') projection@StreamProjection{..} = do
events <- getEvents' (eventsStartingAt streamProjectionKey $ streamProjectionPosition + 1)
return $ foldl' streamProjectionEventHandler projection events
serializedProjection
:: Projection state event
-> Serializer event serialized
-> Projection state serialized
serializedProjection proj Serializer{..} = projectionMapMaybe deserialize proj
projectionMapMaybe
:: (eventB -> Maybe eventA)
-> Projection state eventA
-> Projection state eventB
projectionMapMaybe f (Projection seed handler) = Projection seed handler'
where
handler' state = maybe state (handler state) . f