eventful-core 0.1.3 → 0.2.0
raw patch · 12 files changed
+273/−397 lines, 12 filesdep −hlintdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies removed: hlint
Dependency ranges changed: base
API changes (from Hackage documentation)
- Eventful.Projection: GloballyOrderedProjection :: !(Projection state (GloballyOrderedEvent serialized)) -> !SequenceNumber -> !state -> GloballyOrderedProjection state serialized
- Eventful.Projection: [globallyOrderedProjectionProjection] :: GloballyOrderedProjection state serialized -> !(Projection state (GloballyOrderedEvent serialized))
- Eventful.Projection: [globallyOrderedProjectionSequenceNumber] :: GloballyOrderedProjection state serialized -> !SequenceNumber
- Eventful.Projection: [globallyOrderedProjectionState] :: GloballyOrderedProjection state serialized -> !state
- Eventful.Projection: [streamProjectionUuid] :: StreamProjection state event -> !UUID
- Eventful.Projection: [streamProjectionVersion] :: StreamProjection state event -> EventVersion
- Eventful.Projection: data GloballyOrderedProjection state serialized
- Eventful.Projection: getLatestGlobalProjection :: (Monad m) => GloballyOrderedEventStore serialized m -> GloballyOrderedProjection state serialized -> m (GloballyOrderedProjection state serialized)
- Eventful.Projection: getLatestProjection :: (Monad m) => EventStore event m -> StreamProjection state event -> m (StreamProjection state event)
- Eventful.Projection: globallyOrderedProjection :: Projection state (GloballyOrderedEvent serialized) -> GloballyOrderedProjection state serialized
- Eventful.Projection: globallyOrderedProjectionEventHandler :: GloballyOrderedProjection state serialized -> GloballyOrderedEvent serialized -> GloballyOrderedProjection state serialized
- Eventful.ProjectionCache.Types: getLatestProjectionWithCache :: (Monad m) => EventStore event m -> StreamProjectionCache state m -> StreamProjection state event -> m (StreamProjection state event)
- Eventful.ProjectionCache.Types: type GloballyOrderedProjectionCache key serialized m = ProjectionCache key SequenceNumber serialized m
- Eventful.ProjectionCache.Types: type StreamProjectionCache serialized m = ProjectionCache UUID EventVersion serialized m
- Eventful.Store.Class: EventStore :: (UUID -> m EventVersion) -> (UUID -> EventStoreQueryRange EventVersion -> m [StoredEvent serialized]) -> (ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)) -> EventStore serialized m
- Eventful.Store.Class: GloballyOrderedEvent :: !UUID -> !EventVersion -> !SequenceNumber -> !event -> GloballyOrderedEvent event
- Eventful.Store.Class: GloballyOrderedEventStore :: (EventStoreQueryRange SequenceNumber -> m [GloballyOrderedEvent serialized]) -> GloballyOrderedEventStore serialized m
- Eventful.Store.Class: ProjectionEvent :: !UUID -> !event -> ProjectionEvent event
- Eventful.Store.Class: StoredEvent :: !UUID -> !EventVersion -> !event -> StoredEvent event
- Eventful.Store.Class: [getLatestVersion] :: EventStore serialized m -> UUID -> m EventVersion
- Eventful.Store.Class: [getSequencedEvents] :: GloballyOrderedEventStore serialized m -> EventStoreQueryRange SequenceNumber -> m [GloballyOrderedEvent serialized]
- Eventful.Store.Class: [globallyOrderedEventEvent] :: GloballyOrderedEvent event -> !event
- Eventful.Store.Class: [globallyOrderedEventProjectionId] :: GloballyOrderedEvent event -> !UUID
- Eventful.Store.Class: [globallyOrderedEventSequenceNumber] :: GloballyOrderedEvent event -> !SequenceNumber
- Eventful.Store.Class: [globallyOrderedEventVersion] :: GloballyOrderedEvent event -> !EventVersion
- Eventful.Store.Class: [projectionEventEvent] :: ProjectionEvent event -> !event
- Eventful.Store.Class: [projectionEventProjectionId] :: ProjectionEvent event -> !UUID
- Eventful.Store.Class: [storedEventEvent] :: StoredEvent event -> !event
- Eventful.Store.Class: [storedEventProjectionId] :: StoredEvent event -> !UUID
- Eventful.Store.Class: [storedEventVersion] :: StoredEvent event -> !EventVersion
- Eventful.Store.Class: data EventStore serialized m
- Eventful.Store.Class: data GloballyOrderedEvent event
- Eventful.Store.Class: data ProjectionEvent event
- Eventful.Store.Class: data StoredEvent event
- Eventful.Store.Class: globallyOrderedEventToProjectionEvent :: GloballyOrderedEvent event -> ProjectionEvent event
- Eventful.Store.Class: globallyOrderedEventToStoredEvent :: GloballyOrderedEvent event -> StoredEvent event
- Eventful.Store.Class: instance Data.Foldable.Foldable Eventful.Store.Class.GloballyOrderedEvent
- Eventful.Store.Class: instance Data.Foldable.Foldable Eventful.Store.Class.ProjectionEvent
- Eventful.Store.Class: instance Data.Foldable.Foldable Eventful.Store.Class.StoredEvent
- Eventful.Store.Class: instance Data.Traversable.Traversable Eventful.Store.Class.GloballyOrderedEvent
- Eventful.Store.Class: instance Data.Traversable.Traversable Eventful.Store.Class.ProjectionEvent
- Eventful.Store.Class: instance Data.Traversable.Traversable Eventful.Store.Class.StoredEvent
- Eventful.Store.Class: instance GHC.Base.Functor Eventful.Store.Class.GloballyOrderedEvent
- Eventful.Store.Class: instance GHC.Base.Functor Eventful.Store.Class.ProjectionEvent
- Eventful.Store.Class: instance GHC.Base.Functor Eventful.Store.Class.StoredEvent
- Eventful.Store.Class: instance GHC.Classes.Eq event => GHC.Classes.Eq (Eventful.Store.Class.GloballyOrderedEvent event)
- Eventful.Store.Class: instance GHC.Classes.Eq event => GHC.Classes.Eq (Eventful.Store.Class.ProjectionEvent event)
- Eventful.Store.Class: instance GHC.Classes.Eq event => GHC.Classes.Eq (Eventful.Store.Class.StoredEvent event)
- Eventful.Store.Class: instance GHC.Show.Show event => GHC.Show.Show (Eventful.Store.Class.GloballyOrderedEvent event)
- Eventful.Store.Class: instance GHC.Show.Show event => GHC.Show.Show (Eventful.Store.Class.ProjectionEvent event)
- Eventful.Store.Class: instance GHC.Show.Show event => GHC.Show.Show (Eventful.Store.Class.StoredEvent event)
- Eventful.Store.Class: newtype GloballyOrderedEventStore serialized m
- Eventful.Store.Class: runEventStoreUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> EventStore serialized mstore -> EventStore serialized m
- Eventful.Store.Class: runGloballyOrderedEventStoreUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> GloballyOrderedEventStore serialized mstore -> GloballyOrderedEventStore serialized m
- Eventful.Store.Class: serializedEventStore :: (Monad m) => Serializer event serialized -> EventStore serialized m -> EventStore event m
- Eventful.Store.Class: serializedGloballyOrderedEventStore :: (Monad m) => Serializer event serialized -> GloballyOrderedEventStore serialized m -> GloballyOrderedEventStore event m
- Eventful.Store.Class: storedEventToGloballyOrderedEvent :: SequenceNumber -> StoredEvent event -> GloballyOrderedEvent event
- Eventful.Store.Class: storedEventToProjectionEvent :: StoredEvent event -> ProjectionEvent event
- Eventful.Store.Queries: EventStoreQueryRange :: EventStoreQueryStart a -> EventStoreQueryLimit a -> EventStoreQueryRange a
- Eventful.Store.Queries: [eventStoreQueryRangeLimit] :: EventStoreQueryRange a -> EventStoreQueryLimit a
- Eventful.Store.Queries: [eventStoreQueryRangeStart] :: EventStoreQueryRange a -> EventStoreQueryStart a
- Eventful.Store.Queries: data EventStoreQueryLimit a
- Eventful.Store.Queries: data EventStoreQueryRange a
- Eventful.Store.Queries: data EventStoreQueryStart a
- Eventful.Store.Queries: instance GHC.Base.Functor Eventful.Store.Queries.EventStoreQueryLimit
- Eventful.Store.Queries: instance GHC.Base.Functor Eventful.Store.Queries.EventStoreQueryRange
- Eventful.Store.Queries: instance GHC.Base.Functor Eventful.Store.Queries.EventStoreQueryStart
- Eventful.Store.Queries: instance GHC.Classes.Eq a => GHC.Classes.Eq (Eventful.Store.Queries.EventStoreQueryLimit a)
- Eventful.Store.Queries: instance GHC.Classes.Eq a => GHC.Classes.Eq (Eventful.Store.Queries.EventStoreQueryStart a)
- Eventful.Store.Queries: instance GHC.Show.Show a => GHC.Show.Show (Eventful.Store.Queries.EventStoreQueryLimit a)
- Eventful.Store.Queries: instance GHC.Show.Show a => GHC.Show.Show (Eventful.Store.Queries.EventStoreQueryStart a)
+ Eventful.Projection: [streamProjectionKey] :: StreamProjection key position state event -> !key
+ Eventful.Projection: [streamProjectionPosition] :: StreamProjection key position state event -> !position
+ Eventful.Projection: getLatestStreamProjection :: (Monad m, Num position) => EventStoreReader key position m (StreamEvent key position event) -> StreamProjection key position state event -> m (StreamProjection key position state event)
+ Eventful.Projection: globalStreamProjection :: Projection state (VersionedStreamEvent event) -> GlobalStreamProjection state event
+ Eventful.Projection: type GlobalStreamProjection state event = StreamProjection () SequenceNumber state (VersionedStreamEvent event)
+ Eventful.Projection: type VersionedStreamProjection = StreamProjection UUID EventVersion
+ Eventful.Projection: versionedStreamProjection :: UUID -> Projection state event -> VersionedStreamProjection state event
+ Eventful.ProjectionCache.Types: getLatestVersionedProjectionWithCache :: (Monad m) => VersionedEventStoreReader m event -> VersionedProjectionCache state m -> VersionedStreamProjection state event -> m (VersionedStreamProjection state event)
+ Eventful.ProjectionCache.Types: type GlobalStreamProjectionCache key serialized m = ProjectionCache key SequenceNumber serialized m
+ Eventful.ProjectionCache.Types: type VersionedProjectionCache serialized m = ProjectionCache UUID EventVersion serialized m
+ Eventful.Serializer: traverseSerializer :: (Traversable t) => Serializer a b -> Serializer (t a) (t b)
+ Eventful.Store.Class: EventStoreReader :: (QueryRange key position -> m [event]) -> EventStoreReader key position m event
+ Eventful.Store.Class: EventStoreWriter :: (ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError)) -> EventStoreWriter m event
+ Eventful.Store.Class: StreamEvent :: !key -> !position -> !event -> StreamEvent key position event
+ Eventful.Store.Class: [streamEventEvent] :: StreamEvent key position event -> !event
+ Eventful.Store.Class: [streamEventKey] :: StreamEvent key position event -> !key
+ Eventful.Store.Class: [streamEventPosition] :: StreamEvent key position event -> !position
+ Eventful.Store.Class: data StreamEvent key position event
+ Eventful.Store.Class: instance (GHC.Classes.Eq event, GHC.Classes.Eq position, GHC.Classes.Eq key) => GHC.Classes.Eq (Eventful.Store.Class.StreamEvent key position event)
+ Eventful.Store.Class: instance (GHC.Show.Show event, GHC.Show.Show position, GHC.Show.Show key) => GHC.Show.Show (Eventful.Store.Class.StreamEvent key position event)
+ Eventful.Store.Class: instance Data.Foldable.Foldable (Eventful.Store.Class.StreamEvent key position)
+ Eventful.Store.Class: instance Data.Functor.Contravariant.Contravariant (Eventful.Store.Class.EventStoreWriter m)
+ Eventful.Store.Class: instance Data.Traversable.Traversable (Eventful.Store.Class.StreamEvent key position)
+ Eventful.Store.Class: instance GHC.Base.Functor (Eventful.Store.Class.StreamEvent key position)
+ Eventful.Store.Class: instance GHC.Base.Functor m => GHC.Base.Functor (Eventful.Store.Class.EventStoreReader key position m)
+ Eventful.Store.Class: newtype EventStoreReader key position m event
+ Eventful.Store.Class: newtype EventStoreWriter m event
+ Eventful.Store.Class: runEventStoreReaderUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> EventStoreReader key position mstore event -> EventStoreReader key position m event
+ Eventful.Store.Class: runEventStoreWriterUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> EventStoreWriter mstore event -> EventStoreWriter m event
+ Eventful.Store.Class: serializedEventStoreReader :: (Monad m) => Serializer event serialized -> EventStoreReader key position m serialized -> EventStoreReader key position m event
+ Eventful.Store.Class: serializedEventStoreWriter :: (Monad m) => Serializer event serialized -> EventStoreWriter m serialized -> EventStoreWriter m event
+ Eventful.Store.Class: serializedGlobalEventStoreReader :: (Monad m) => Serializer event serialized -> GlobalEventStoreReader m serialized -> GlobalEventStoreReader m event
+ Eventful.Store.Class: serializedVersionedEventStoreReader :: (Monad m) => Serializer event serialized -> VersionedEventStoreReader m serialized -> VersionedEventStoreReader m event
+ Eventful.Store.Class: type GlobalEventStoreReader m event = EventStoreReader () SequenceNumber m (GlobalStreamEvent event)
+ Eventful.Store.Class: type GlobalStreamEvent event = StreamEvent () SequenceNumber (VersionedStreamEvent event)
+ Eventful.Store.Class: type VersionedEventStoreReader m event = EventStoreReader UUID EventVersion m (VersionedStreamEvent event)
+ Eventful.Store.Class: type VersionedStreamEvent event = StreamEvent UUID EventVersion event
+ Eventful.Store.Queries: QueryRange :: key -> QueryStart position -> QueryLimit position -> QueryRange key position
+ Eventful.Store.Queries: [queryRangeKey] :: QueryRange key position -> key
+ Eventful.Store.Queries: [queryRangeLimit] :: QueryRange key position -> QueryLimit position
+ Eventful.Store.Queries: [queryRangeStart] :: QueryRange key position -> QueryStart position
+ Eventful.Store.Queries: data QueryLimit position
+ Eventful.Store.Queries: data QueryRange key position
+ Eventful.Store.Queries: data QueryStart position
+ Eventful.Store.Queries: instance (GHC.Classes.Eq position, GHC.Classes.Eq key) => GHC.Classes.Eq (Eventful.Store.Queries.QueryRange key position)
+ Eventful.Store.Queries: instance (GHC.Show.Show position, GHC.Show.Show key) => GHC.Show.Show (Eventful.Store.Queries.QueryRange key position)
+ Eventful.Store.Queries: instance GHC.Base.Functor Eventful.Store.Queries.QueryLimit
+ Eventful.Store.Queries: instance GHC.Base.Functor Eventful.Store.Queries.QueryStart
+ Eventful.Store.Queries: instance GHC.Classes.Eq position => GHC.Classes.Eq (Eventful.Store.Queries.QueryLimit position)
+ Eventful.Store.Queries: instance GHC.Classes.Eq position => GHC.Classes.Eq (Eventful.Store.Queries.QueryStart position)
+ Eventful.Store.Queries: instance GHC.Show.Show position => GHC.Show.Show (Eventful.Store.Queries.QueryLimit position)
+ Eventful.Store.Queries: instance GHC.Show.Show position => GHC.Show.Show (Eventful.Store.Queries.QueryStart position)
- Eventful.Aggregate: commandStoredAggregate :: (Monad m) => EventStore serialized m -> Aggregate state serialized command -> UUID -> command -> m [serialized]
+ Eventful.Aggregate: commandStoredAggregate :: (Monad m) => EventStoreWriter m event -> VersionedEventStoreReader m event -> Aggregate state event command -> UUID -> command -> m [event]
- Eventful.EventBus: storeAndPublishEvents :: (Monad m) => EventStore serialized m -> [UUID -> serialized -> m ()] -> ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)
+ Eventful.EventBus: storeAndPublishEvents :: (Monad m) => EventStoreWriter m event -> [UUID -> event -> m ()] -> ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError)
- Eventful.EventBus: synchronousEventBusWrapper :: (Monad m) => EventStore serialized m -> [EventStore serialized m -> UUID -> serialized -> m ()] -> EventStore serialized m
+ Eventful.EventBus: synchronousEventBusWrapper :: (Monad m) => EventStoreWriter m event -> [EventStoreWriter m event -> UUID -> event -> m ()] -> EventStoreWriter m event
- Eventful.ProcessManager: ProcessManager :: Projection state (ProjectionEvent event) -> (state -> [ProcessManagerCommand event command]) -> (state -> [ProjectionEvent event]) -> ProcessManager state event command
+ Eventful.ProcessManager: ProcessManager :: Projection state (VersionedStreamEvent event) -> (state -> [ProcessManagerCommand event command]) -> (state -> [StreamEvent UUID () event]) -> ProcessManager state event command
- Eventful.ProcessManager: [processManagerPendingEvents] :: ProcessManager state event command -> state -> [ProjectionEvent event]
+ Eventful.ProcessManager: [processManagerPendingEvents] :: ProcessManager state event command -> state -> [StreamEvent UUID () event]
- Eventful.ProcessManager: [processManagerProjection] :: ProcessManager state event command -> Projection state (ProjectionEvent event)
+ Eventful.ProcessManager: [processManagerProjection] :: ProcessManager state event command -> Projection state (VersionedStreamEvent event)
- Eventful.ProcessManager: applyProcessManagerCommandsAndEvents :: (Monad m) => ProcessManager state event command -> EventStore event m -> state -> m ()
+ Eventful.ProcessManager: applyProcessManagerCommandsAndEvents :: (Monad m) => ProcessManager state event command -> EventStoreWriter m event -> VersionedEventStoreReader m event -> state -> m ()
- Eventful.Projection: StreamProjection :: Projection state event -> !UUID -> EventVersion -> !state -> StreamProjection state event
+ Eventful.Projection: StreamProjection :: !key -> !position -> !(Projection state event) -> !state -> StreamProjection key position state event
- Eventful.Projection: [streamProjectionProjection] :: StreamProjection state event -> Projection state event
+ Eventful.Projection: [streamProjectionProjection] :: StreamProjection key position state event -> !(Projection state event)
- Eventful.Projection: [streamProjectionState] :: StreamProjection state event -> !state
+ Eventful.Projection: [streamProjectionState] :: StreamProjection key position state event -> !state
- Eventful.Projection: data StreamProjection state event
+ Eventful.Projection: data StreamProjection key position state event
- Eventful.Projection: streamProjection :: Projection state event -> UUID -> StreamProjection state event
+ Eventful.Projection: streamProjection :: key -> position -> Projection state event -> StreamProjection key position state event
- Eventful.ProjectionCache.Types: ProjectionCache :: (key -> orderKey -> serialized -> m ()) -> (key -> m (Maybe (orderKey, serialized))) -> ProjectionCache key orderKey serialized m
+ Eventful.ProjectionCache.Types: ProjectionCache :: (key -> position -> serialized -> m ()) -> (key -> m (Maybe (position, serialized))) -> ProjectionCache key position serialized m
- Eventful.ProjectionCache.Types: [loadProjectionSnapshot] :: ProjectionCache key orderKey serialized m -> key -> m (Maybe (orderKey, serialized))
+ Eventful.ProjectionCache.Types: [loadProjectionSnapshot] :: ProjectionCache key position serialized m -> key -> m (Maybe (position, serialized))
- Eventful.ProjectionCache.Types: [storeProjectionSnapshot] :: ProjectionCache key orderKey serialized m -> key -> orderKey -> serialized -> m ()
+ Eventful.ProjectionCache.Types: [storeProjectionSnapshot] :: ProjectionCache key position serialized m -> key -> position -> serialized -> m ()
- Eventful.ProjectionCache.Types: data ProjectionCache key orderKey serialized m
+ Eventful.ProjectionCache.Types: data ProjectionCache key position serialized m
- Eventful.ProjectionCache.Types: getLatestGlobalProjectionWithCache :: (Monad m) => GloballyOrderedEventStore event m -> GloballyOrderedProjectionCache key state m -> GloballyOrderedProjection state event -> key -> m (GloballyOrderedProjection state event)
+ Eventful.ProjectionCache.Types: getLatestGlobalProjectionWithCache :: (Monad m) => GlobalEventStoreReader m event -> GlobalStreamProjectionCache key state m -> GlobalStreamProjection state event -> key -> m (GlobalStreamProjection state event)
- Eventful.ProjectionCache.Types: runProjectionCacheUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> ProjectionCache key orderKey serialized mstore -> ProjectionCache key orderKey serialized m
+ Eventful.ProjectionCache.Types: runProjectionCacheUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a) -> ProjectionCache key position serialized mstore -> ProjectionCache key position serialized m
- Eventful.ProjectionCache.Types: serializedProjectionCache :: (Monad m) => Serializer state serialized -> ProjectionCache key orderKey serialized m -> ProjectionCache key orderKey state m
+ Eventful.ProjectionCache.Types: serializedProjectionCache :: (Monad m) => Serializer state serialized -> ProjectionCache key position serialized m -> ProjectionCache key position state m
- Eventful.ProjectionCache.Types: updateGlobalProjectionCache :: (Monad m) => GloballyOrderedEventStore event m -> GloballyOrderedProjectionCache key state m -> GloballyOrderedProjection state event -> key -> m ()
+ Eventful.ProjectionCache.Types: updateGlobalProjectionCache :: (Monad m) => GlobalEventStoreReader m event -> GlobalStreamProjectionCache key state m -> GlobalStreamProjection state event -> key -> m ()
- Eventful.ProjectionCache.Types: updateProjectionCache :: (Monad m) => EventStore event m -> StreamProjectionCache state m -> StreamProjection state event -> m ()
+ Eventful.ProjectionCache.Types: updateProjectionCache :: (Monad m) => VersionedEventStoreReader m event -> VersionedProjectionCache state m -> VersionedStreamProjection state event -> m ()
- Eventful.ReadModel.Class: ReadModel :: model -> (model -> m SequenceNumber) -> (model -> [GloballyOrderedEvent serialized] -> m ()) -> ReadModel model serialized m
+ Eventful.ReadModel.Class: ReadModel :: model -> (model -> m SequenceNumber) -> (model -> [GlobalStreamEvent serialized] -> m ()) -> ReadModel model serialized m
- Eventful.ReadModel.Class: [readModelHandleEvents] :: ReadModel model serialized m -> model -> [GloballyOrderedEvent serialized] -> m ()
+ Eventful.ReadModel.Class: [readModelHandleEvents] :: ReadModel model serialized m -> model -> [GlobalStreamEvent serialized] -> m ()
- Eventful.ReadModel.Class: runPollingReadModel :: (MonadIO m, Monad mstore) => ReadModel model serialized m -> GloballyOrderedEventStore serialized mstore -> (forall a. mstore a -> m a) -> PollingPeriodSeconds -> m ()
+ Eventful.ReadModel.Class: runPollingReadModel :: (MonadIO m, Monad mstore) => ReadModel model serialized m -> GlobalEventStoreReader mstore serialized -> (forall a. mstore a -> m a) -> PollingPeriodSeconds -> m ()
- Eventful.Store.Class: [getEvents] :: EventStore serialized m -> UUID -> EventStoreQueryRange EventVersion -> m [StoredEvent serialized]
+ Eventful.Store.Class: [getEvents] :: EventStoreReader key position m event -> QueryRange key position -> m [event]
- Eventful.Store.Class: [storeEvents] :: EventStore serialized m -> ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)
+ Eventful.Store.Class: [storeEvents] :: EventStoreWriter m event -> ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError)
- Eventful.Store.Class: transactionalExpectedWriteHelper :: (Monad m) => (UUID -> m EventVersion) -> (UUID -> [serialized] -> m ()) -> ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)
+ Eventful.Store.Class: transactionalExpectedWriteHelper :: (Monad m) => (UUID -> m EventVersion) -> (UUID -> [event] -> m ()) -> ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError)
- Eventful.Store.Queries: MaxNumberOfEvents :: Int -> EventStoreQueryLimit a
+ Eventful.Store.Queries: MaxNumberOfEvents :: Int -> QueryLimit position
- Eventful.Store.Queries: NoQueryLimit :: EventStoreQueryLimit a
+ Eventful.Store.Queries: NoQueryLimit :: QueryLimit position
- Eventful.Store.Queries: StartFromBeginning :: EventStoreQueryStart a
+ Eventful.Store.Queries: StartFromBeginning :: QueryStart position
- Eventful.Store.Queries: StartQueryAt :: a -> EventStoreQueryStart a
+ Eventful.Store.Queries: StartQueryAt :: position -> QueryStart position
- Eventful.Store.Queries: StopQueryAt :: a -> EventStoreQueryLimit a
+ Eventful.Store.Queries: StopQueryAt :: position -> QueryLimit position
- Eventful.Store.Queries: allEvents :: EventStoreQueryRange a
+ Eventful.Store.Queries: allEvents :: key -> QueryRange key position
- Eventful.Store.Queries: eventsStartingAt :: a -> EventStoreQueryRange a
+ Eventful.Store.Queries: eventsStartingAt :: key -> position -> QueryRange key position
- Eventful.Store.Queries: eventsStartingAtTakeLimit :: a -> Int -> EventStoreQueryRange a
+ Eventful.Store.Queries: eventsStartingAtTakeLimit :: key -> position -> Int -> QueryRange key position
- Eventful.Store.Queries: eventsStartingAtUntil :: a -> a -> EventStoreQueryRange a
+ Eventful.Store.Queries: eventsStartingAtUntil :: key -> position -> position -> QueryRange key position
- Eventful.Store.Queries: eventsUntil :: a -> EventStoreQueryRange a
+ Eventful.Store.Queries: eventsUntil :: key -> position -> QueryRange key position
Files
- eventful-core.cabal +1/−26
- src/Eventful/Aggregate.hs +7/−6
- src/Eventful/EventBus.hs +9/−15
- src/Eventful/ProcessManager.hs +8/−7
- src/Eventful/Projection.hs +57/−82
- src/Eventful/ProjectionCache/Types.hs +57/−60
- src/Eventful/ReadModel/Class.hs +4/−4
- src/Eventful/Serializer.hs +14/−0
- src/Eventful/Store/Class.hs +89/−152
- src/Eventful/Store/Queries.hs +26/−24
- tests/Eventful/SerializerSpec.hs +1/−0
- tests/HLint.hs +0/−21
eventful-core.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: eventful-core-version: 0.1.3+version: 0.2.0 synopsis: Core module for eventful description: Core module for eventful category: Database,Eventsourcing@@ -80,7 +80,6 @@ , HUnit other-modules: Eventful.SerializerSpec- HLint Eventful Eventful.Aggregate Eventful.EventBus@@ -95,28 +94,4 @@ Eventful.TH.Projection Eventful.TH.SumTypeSerializer Eventful.UUID- default-language: Haskell2010--test-suite style- type: exitcode-stdio-1.0- main-is: HLint.hs- hs-source-dirs:- tests- ghc-options: -Wall- build-depends:- base >= 4.9 && < 5- , aeson- , containers- , contravariant- , http-api-data- , path-pieces- , sum-type-boilerplate- , template-haskell- , text- , transformers- , uuid- , hlint- other-modules:- Eventful.SerializerSpec- Spec default-language: Haskell2010
src/Eventful/Aggregate.hs view
@@ -44,15 +44,16 @@ -- saves the events back to the store as well. commandStoredAggregate :: (Monad m)- => EventStore serialized m- -> Aggregate state serialized command+ => EventStoreWriter m event+ -> VersionedEventStoreReader m event+ -> Aggregate state event command -> UUID -> command- -> m [serialized]-commandStoredAggregate store (Aggregate handler proj) uuid command = do- StreamProjection{..} <- getLatestProjection store (streamProjection proj uuid)+ -> m [event]+commandStoredAggregate writer reader (Aggregate handler proj) uuid command = do+ StreamProjection{..} <- getLatestStreamProjection reader (versionedStreamProjection uuid proj) let events = handler streamProjectionState command- mError <- storeEvents store (ExactVersion streamProjectionVersion) uuid events+ mError <- storeEvents writer (ExactVersion streamProjectionPosition) uuid events case mError of (Just err) -> error $ "TODO: Create aggregate restart logic. " ++ show err Nothing -> return events
src/Eventful/EventBus.hs view
@@ -12,31 +12,25 @@ -- is also useful for integration testing along with an in-memory event store. synchronousEventBusWrapper :: (Monad m)- => EventStore serialized m- -> [EventStore serialized m -> UUID -> serialized -> m ()]- -> EventStore serialized m-synchronousEventBusWrapper store handlers =- let+ => EventStoreWriter m event+ -> [EventStoreWriter m event -> UUID -> event -> m ()]+ -> EventStoreWriter m event+synchronousEventBusWrapper writer handlers = wrappedStore+ where -- NB: We need to use recursive let bindings so we can put wrappedStore -- inside the event handlers handlers' = map ($ wrappedStore) handlers- wrappedStore =- EventStore- { getLatestVersion = getLatestVersion store- , getEvents = getEvents store- , storeEvents = storeAndPublishEvents store handlers'- }- in wrappedStore+ wrappedStore = EventStoreWriter $ storeAndPublishEvents writer handlers' -- | Stores events in the store and them publishes them to the event handlers. -- This is used in the 'synchronousEventBusWrapper'. storeAndPublishEvents :: (Monad m)- => EventStore serialized m- -> [UUID -> serialized -> m ()]+ => EventStoreWriter m event+ -> [UUID -> event -> m ()] -> ExpectedVersion -> UUID- -> [serialized]+ -> [event] -> m (Maybe EventWriteError) storeAndPublishEvents store handlers expectedVersion uuid events = do result <- storeEvents store expectedVersion uuid events
src/Eventful/ProcessManager.hs view
@@ -21,9 +21,9 @@ -- appropriate Aggregates or Projections in other streams. data ProcessManager state event command = ProcessManager- { processManagerProjection :: Projection state (ProjectionEvent event)+ { processManagerProjection :: Projection state (VersionedStreamEvent event) , processManagerPendingCommands :: state -> [ProcessManagerCommand event command]- , processManagerPendingEvents :: state -> [ProjectionEvent event]+ , processManagerPendingEvents :: state -> [StreamEvent UUID () event] } -- | This is a @command@ along with the UUID of the target 'Aggregate', and@@ -46,11 +46,12 @@ applyProcessManagerCommandsAndEvents :: (Monad m) => ProcessManager state event command- -> EventStore event m+ -> EventStoreWriter m event+ -> VersionedEventStoreReader m event -> state -> m ()-applyProcessManagerCommandsAndEvents ProcessManager{..} store state = do+applyProcessManagerCommandsAndEvents ProcessManager{..} writer reader state = do forM_ (processManagerPendingCommands state) $ \(ProcessManagerCommand aggregateId aggregate command) ->- void $ commandStoredAggregate store aggregate aggregateId command- forM_ (processManagerPendingEvents state) $ \(ProjectionEvent projectionId event) ->- storeEvents store AnyVersion projectionId [event]+ void $ commandStoredAggregate writer reader aggregate aggregateId command+ forM_ (processManagerPendingEvents state) $ \(StreamEvent projectionId () event) ->+ storeEvents writer AnyVersion projectionId [event]
src/Eventful/Projection.hs view
@@ -5,16 +5,15 @@ , latestProjection , allProjections , StreamProjection (..)+ , VersionedStreamProjection+ , GlobalStreamProjection , streamProjection- , getLatestProjection- , GloballyOrderedProjection (..)- , globallyOrderedProjection- , globallyOrderedProjectionEventHandler- , getLatestGlobalProjection+ , versionedStreamProjection+ , globalStreamProjection+ , getLatestStreamProjection , serializedProjection , projectionMapMaybe- )- where+ ) where import Data.Foldable (foldl') import Data.Functor.Contravariant@@ -56,91 +55,67 @@ allProjections (Projection seed handler) = scanl' handler seed -- | A 'StreamProjection' is a 'Projection' that has been constructed from--- events from a particular event stream. This is mostly useful so we can--- associate an 'EventVersion' with some state.-data StreamProjection state event+-- events from a particular event stream. This is useful when we want to cache+-- the resulting state and also keep track of what part of the stream the state+-- is caught up to.+data StreamProjection key position state event = StreamProjection- { streamProjectionProjection :: Projection state event- , streamProjectionUuid :: !UUID- , streamProjectionVersion :: EventVersion+ { streamProjectionKey :: !key+ , streamProjectionPosition :: !position+ , streamProjectionProjection :: !(Projection state event) , streamProjectionState :: !state } --- | Initialize a 'StreamProjection' with a 'Projection'.-streamProjection- :: Projection state event- -> UUID- -> StreamProjection state event-streamProjection projection@Projection{..} uuid =- StreamProjection projection uuid (-1) projectionSeed+type VersionedStreamProjection = StreamProjection UUID EventVersion+type GlobalStreamProjection state event = StreamProjection () SequenceNumber state (VersionedStreamEvent event) --- | Gets the latest projection from a store by using 'getEvents' and then--- applying the events using the Projection's event handler.-getLatestProjection- :: (Monad m)- => EventStore event m- -> StreamProjection state event- -> m (StreamProjection state event)-getLatestProjection store projection@StreamProjection{..} = do- events <- getEvents store streamProjectionUuid (eventsStartingAt $ streamProjectionVersion + 1)- let- latestVersion = newEventVersion events- latestState = foldl' (projectionEventHandler streamProjectionProjection) streamProjectionState $ storedEventEvent <$> events- return $- projection- { streamProjectionVersion = latestVersion- , streamProjectionState = latestState- }- where- newEventVersion [] = streamProjectionVersion- newEventVersion es = maximum $ storedEventVersion <$> es+-- | Initialize a 'StreamProjection' with a 'Projection', key, and order key.+streamProjection+ :: key+ -> position+ -> Projection state event+ -> StreamProjection key position state event+streamProjection key position projection@Projection{..} =+ StreamProjection key position projection projectionSeed --- | This is a combination of a 'Projection' and the latest projection state--- with respect to some 'SequenceNumber'. This is useful for in-memory read--- models, and for querying the latest state starting from some previous state--- at a lower 'SequenceNumber'.-data GloballyOrderedProjection state serialized- = GloballyOrderedProjection- { globallyOrderedProjectionProjection :: !(Projection state (GloballyOrderedEvent serialized))- , globallyOrderedProjectionSequenceNumber :: !SequenceNumber- , globallyOrderedProjectionState :: !state- }+-- | Initialize a 'VersionedStreamProjection'.+versionedStreamProjection+ :: UUID+ -> Projection state event+ -> VersionedStreamProjection state event+versionedStreamProjection uuid = streamProjection uuid (-1) --- | Initialize a 'GloballyOrderedProjection' at 'SequenceNumber' 0 and with--- the projection's seed value.-globallyOrderedProjection- :: Projection state (GloballyOrderedEvent serialized)- -> GloballyOrderedProjection state serialized-globallyOrderedProjection projection@Projection{..} =- GloballyOrderedProjection projection 0 projectionSeed+-- | Initialize a 'GlobalStreamProjection'.+globalStreamProjection+ :: Projection state (VersionedStreamEvent event)+ -> GlobalStreamProjection state event+globalStreamProjection = streamProjection () 0 --- | This applies an event to a 'GloballyOrderedProjection'. NOTE: There is no--- guarantee that the 'SequenceNumber' for the event is the previous--- 'SequenceNumber' plus one (in fact, that isn't even a guarantee that some--- stores can provide). This function will update the--- 'GloballyOrderedProjetion' to use the sequence number of the event.-globallyOrderedProjectionEventHandler- :: GloballyOrderedProjection state serialized- -> GloballyOrderedEvent serialized- -> GloballyOrderedProjection state serialized-globallyOrderedProjectionEventHandler GloballyOrderedProjection{..} event@GloballyOrderedEvent{..} =+-- | Apply an event to the 'StreamProjection'. NOTE: There is no guarantee that+-- the order key for the event is greater than the current order key in the+-- 'StreamProjection'. This function simple will update the 'StreamProjection'+-- to use the order key of the event.+streamProjectionEventHandler+ :: StreamProjection key position state event+ -> StreamEvent eventKey position event+ -> StreamProjection key position state event+streamProjectionEventHandler StreamProjection{..} event = let- Projection{..} = globallyOrderedProjectionProjection- seqNum = globallyOrderedEventSequenceNumber- state' = projectionEventHandler globallyOrderedProjectionState event- in GloballyOrderedProjection globallyOrderedProjectionProjection seqNum state'+ Projection{..} = streamProjectionProjection+ position' = streamEventPosition event+ state' = projectionEventHandler streamProjectionState (streamEventEvent event)+ in StreamProjection streamProjectionKey position' streamProjectionProjection state' --- | Gets globally ordered events from the event store and builds a--- 'Projection' based on 'ProjectionEvent'. Optionally accepts the current--- projection state as an argument.-getLatestGlobalProjection- :: (Monad m)- => GloballyOrderedEventStore serialized m- -> GloballyOrderedProjection state serialized- -> m (GloballyOrderedProjection state serialized)-getLatestGlobalProjection store globalProjection@GloballyOrderedProjection{..} = do- events <- getSequencedEvents store (eventsStartingAt $ globallyOrderedProjectionSequenceNumber + 1)- return $ foldl' globallyOrderedProjectionEventHandler globalProjection events+-- | Gets the latest projection from a store by querying events from the latest+-- order key and then applying the events using the Projection's event handler.+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 -- | Use a 'Serializer' to wrap a 'Projection' with event type @event@ so it -- uses the @serialized@ type.
src/Eventful/ProjectionCache/Types.hs view
@@ -3,11 +3,11 @@ module Eventful.ProjectionCache.Types ( ProjectionCache (..)- , StreamProjectionCache- , GloballyOrderedProjectionCache+ , VersionedProjectionCache+ , GlobalStreamProjectionCache , runProjectionCacheUsing , serializedProjectionCache- , getLatestProjectionWithCache+ , getLatestVersionedProjectionWithCache , getLatestGlobalProjectionWithCache , updateProjectionCache , updateGlobalProjectionCache@@ -27,34 +27,34 @@ -- helper functions in this module to interpret the stored values using a -- 'Projection'. ----- The @key@ and @orderKey@ type parameters are polymorphic so we can abstract+-- The @key@ and @position@ type parameters are polymorphic so we can abstract -- over a cache for individual event streams, and a cache for globally ordered -- streams.-data ProjectionCache key orderKey serialized m+data ProjectionCache key position serialized m = ProjectionCache- { storeProjectionSnapshot :: key -> orderKey -> serialized -> m ()- -- ^ Stores the state for a projection at a given @key@ and @orderKey@.+ { storeProjectionSnapshot :: key -> position -> serialized -> m ()+ -- ^ Stores the state for a projection at a given @key@ and @position@. -- This is pretty unsafe, because there is no guarantee what is stored is -- actually derived from the events in the stream. Consider using -- 'updateProjectionCache'.- , loadProjectionSnapshot :: key -> m (Maybe (orderKey, serialized))+ , loadProjectionSnapshot :: key -> m (Maybe (position, serialized)) -- ^ Loads the latest projection state from the cache. } -- | Type synonym for a 'ProjectionCache' used on individual event streams.-type StreamProjectionCache serialized m = ProjectionCache UUID EventVersion serialized m+type VersionedProjectionCache serialized m = ProjectionCache UUID EventVersion serialized m -- | Type synonym for a 'ProjectionCache' that is used in conjunction with a--- 'GloballyOrderedEventStore'.-type GloballyOrderedProjectionCache key serialized m = ProjectionCache key SequenceNumber serialized m+-- 'GlobalStreamEventStore'.+type GlobalStreamProjectionCache key serialized m = ProjectionCache key SequenceNumber serialized m -- | Changes the monad a 'ProjectionCache' runs in. This is useful to run the -- cache in another 'Monad' while forgetting the original 'Monad'. runProjectionCacheUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a)- -> ProjectionCache key orderKey serialized mstore- -> ProjectionCache key orderKey serialized m+ -> ProjectionCache key position serialized mstore+ -> ProjectionCache key position serialized m runProjectionCacheUsing runCache ProjectionCache{..} = ProjectionCache { storeProjectionSnapshot = \uuid version state -> runCache $ storeProjectionSnapshot uuid version state@@ -67,8 +67,8 @@ serializedProjectionCache :: (Monad m) => Serializer state serialized- -> ProjectionCache key orderKey serialized m- -> ProjectionCache key orderKey state m+ -> ProjectionCache key position serialized m+ -> ProjectionCache key position state m serializedProjectionCache Serializer{..} ProjectionCache{..} = ProjectionCache storeProjectionSnapshot' loadProjectionSnapshot' where@@ -77,71 +77,68 @@ mState <- loadProjectionSnapshot uuid return $ mState >>= traverse deserialize --- | Like 'getLatestProjection', but uses a 'ProjectionCache' if it contains+-- | Like 'getLatestVersionedProjection', but uses a 'ProjectionCache' if it contains -- more recent state.-getLatestProjectionWithCache+getLatestVersionedProjectionWithCache :: (Monad m)- => EventStore event m- -> StreamProjectionCache state m- -> StreamProjection state event- -> m (StreamProjection state event)-getLatestProjectionWithCache store cache originalProj = do- mLatestState <- loadProjectionSnapshot cache (streamProjectionUuid originalProj)- let- mkProjection' (version, state) =- if version > streamProjectionVersion originalProj- then- originalProj- { streamProjectionVersion = version- , streamProjectionState = state- }- else originalProj- projection' = maybe originalProj mkProjection' mLatestState- getLatestProjection store projection'+ => VersionedEventStoreReader m event+ -> VersionedProjectionCache state m+ -> VersionedStreamProjection state event+ -> m (VersionedStreamProjection state event)+getLatestVersionedProjectionWithCache store cache projection =+ getLatestProjectionWithCache' cache projection (streamProjectionKey projection) >>= getLatestStreamProjection store -- | Like 'getLatestGlobalProjection', but uses a 'ProjectionCache' if it -- contains more recent state. getLatestGlobalProjectionWithCache :: (Monad m)- => GloballyOrderedEventStore event m- -> GloballyOrderedProjectionCache key state m- -> GloballyOrderedProjection state event+ => GlobalEventStoreReader m event+ -> GlobalStreamProjectionCache key state m+ -> GlobalStreamProjection state event -> key- -> m (GloballyOrderedProjection state event)-getLatestGlobalProjectionWithCache store cache originalProj key = do+ -> m (GlobalStreamProjection state event)+getLatestGlobalProjectionWithCache store cache projection key =+ getLatestProjectionWithCache' cache projection key >>= getLatestStreamProjection store++getLatestProjectionWithCache'+ :: (Monad m, Ord position)+ => ProjectionCache key position state m+ -> StreamProjection projKey position state event+ -> key+ -> m (StreamProjection projKey position state event)+getLatestProjectionWithCache' cache projection key = do mLatestState <- loadProjectionSnapshot cache key let- mkProjection' (seqNum, state) =- if seqNum > globallyOrderedProjectionSequenceNumber originalProj+ mkProjection' (position, state) =+ if position > streamProjectionPosition projection then- originalProj- { globallyOrderedProjectionSequenceNumber = seqNum- , globallyOrderedProjectionState = state+ projection+ { streamProjectionPosition = position+ , streamProjectionState = state }- else originalProj- projection' = maybe originalProj mkProjection' mLatestState- getLatestGlobalProjection store projection'+ else projection+ return $ maybe projection mkProjection' mLatestState -- | Loads the latest projection state from the cache/store and stores this -- value back into the projection cache. updateProjectionCache :: (Monad m)- => EventStore event m- -> StreamProjectionCache state m- -> StreamProjection state event+ => VersionedEventStoreReader m event+ -> VersionedProjectionCache state m+ -> VersionedStreamProjection state event -> m ()-updateProjectionCache store cache projection = do- StreamProjection{..} <- getLatestProjectionWithCache store cache projection- storeProjectionSnapshot cache streamProjectionUuid streamProjectionVersion streamProjectionState+updateProjectionCache reader cache projection = do+ StreamProjection{..} <- getLatestVersionedProjectionWithCache reader cache projection+ storeProjectionSnapshot cache streamProjectionKey streamProjectionPosition streamProjectionState --- | Analog of 'updateProjectionCache' for a 'GloballyOrderedProjectionCache'.+-- | Analog of 'updateProjectionCache' for a 'GlobalStreamProjectionCache'. updateGlobalProjectionCache :: (Monad m)- => GloballyOrderedEventStore event m- -> GloballyOrderedProjectionCache key state m- -> GloballyOrderedProjection state event+ => GlobalEventStoreReader m event+ -> GlobalStreamProjectionCache key state m+ -> GlobalStreamProjection state event -> key -> m ()-updateGlobalProjectionCache store cache projection key = do- GloballyOrderedProjection{..} <- getLatestGlobalProjectionWithCache store cache projection key- storeProjectionSnapshot cache key globallyOrderedProjectionSequenceNumber globallyOrderedProjectionState+updateGlobalProjectionCache reader cache projection key = do+ StreamProjection{..} <- getLatestGlobalProjectionWithCache reader cache projection key+ storeProjectionSnapshot cache key streamProjectionPosition streamProjectionState
src/Eventful/ReadModel/Class.hs view
@@ -16,7 +16,7 @@ ReadModel { readModelModel :: model , readModelLatestAppliedSequence :: model -> m SequenceNumber- , readModelHandleEvents :: model -> [GloballyOrderedEvent serialized] -> m ()+ , readModelHandleEvents :: model -> [GlobalStreamEvent serialized] -> m () } type PollingPeriodSeconds = Double@@ -24,14 +24,14 @@ runPollingReadModel :: (MonadIO m, Monad mstore) => ReadModel model serialized m- -> GloballyOrderedEventStore serialized mstore+ -> GlobalEventStoreReader mstore serialized -> (forall a. mstore a -> m a) -> PollingPeriodSeconds -> m ()-runPollingReadModel ReadModel{..} getGloballyOrderedEvents runStore waitSeconds = forever $ do+runPollingReadModel ReadModel{..} globalReader runStore waitSeconds = forever $ do -- Get new events starting from latest applied sequence number latestSeq <- readModelLatestAppliedSequence readModelModel- newEvents <- runStore $ getSequencedEvents getGloballyOrderedEvents (eventsStartingAt $ latestSeq + 1)+ newEvents <- runStore $ getEvents globalReader (eventsStartingAt () $ latestSeq + 1) -- Handle the new events readModelHandleEvents readModelModel newEvents
src/Eventful/Serializer.hs view
@@ -11,6 +11,7 @@ , composeSerializers -- * Common serializers , idSerializer+ , traverseSerializer , jsonSerializer , jsonTextSerializer , dynamicSerializer@@ -25,6 +26,7 @@ import Data.Maybe (fromMaybe) import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TLE+import Data.Typeable (typeOf) import GHC.Generics -- | A 'Serializer' describes the injective conversion between types @a@ and@@ -66,6 +68,18 @@ -- serializer but you don't need to actually change types. idSerializer :: Serializer a a idSerializer = simpleSerializer id Just++-- | Uses 'Traversable' to wrap a 'Serializer'.+traverseSerializer+ :: (Traversable t)+ => Serializer a b+ -> Serializer (t a) (t b)+traverseSerializer Serializer{..} =+ Serializer serialize' deserialize' deserializeEither'+ where+ serialize' = fmap serialize+ deserialize' = traverse deserialize+ deserializeEither' = traverse deserializeEither -- | A 'Serializer' for aeson 'Value's. jsonSerializer :: (ToJSON a, FromJSON a) => Serializer a Value
src/Eventful/Store/Class.hs view
@@ -7,24 +7,24 @@ module Eventful.Store.Class ( -- * EventStore- EventStore (..)- , GloballyOrderedEventStore (..)+ EventStoreReader (..)+ , EventStoreWriter (..)+ , VersionedEventStoreReader+ , GlobalEventStoreReader+ , StreamEvent (..)+ , VersionedStreamEvent+ , GlobalStreamEvent , ExpectedVersion (..) , EventWriteError (..)- , runEventStoreUsing- , runGloballyOrderedEventStoreUsing+ , runEventStoreReaderUsing+ , runEventStoreWriterUsing , module Eventful.Store.Queries -- * Serialization- , serializedEventStore- , serializedGloballyOrderedEventStore+ , serializedEventStoreReader+ , serializedVersionedEventStoreReader+ , serializedGlobalEventStoreReader+ , serializedEventStoreWriter -- * Utility types- , ProjectionEvent (..)- , StoredEvent (..)- , storedEventToProjectionEvent- , GloballyOrderedEvent (..)- , globallyOrderedEventToStoredEvent- , globallyOrderedEventToProjectionEvent- , storedEventToGloballyOrderedEvent , EventVersion (..) , SequenceNumber (..) -- * Utility functions@@ -32,6 +32,7 @@ ) where import Data.Aeson+import Data.Functor.Contravariant import Data.Maybe (mapMaybe) import Web.HttpApiData import Web.PathPieces@@ -40,29 +41,36 @@ import Eventful.Store.Queries import Eventful.UUID --- | The 'EventStore' is the core type of eventful. A store operates in some--- monad @m@ and stores events by serializing them to the type @serialized@.-data EventStore serialized m- = EventStore- { getLatestVersion :: UUID -> m EventVersion- -- ^ Gets the latest 'EventVersion' for a given 'Projection'.- , getEvents :: UUID -> EventStoreQueryRange EventVersion -> m [StoredEvent serialized]- -- ^ Retrieves all the events for a given 'Projection' using that- -- projection's UUID. If an event version is provided then all events with- -- a version greater than or equal to that version are returned.- , storeEvents :: ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)- -- ^ Stores the events for a given 'Projection' using that projection's- -- UUID.- }+-- | An 'EventStoreReader' is a function to query a stream from an event store.+-- It operates in some monad @m@ and returns events of type @event@ from a+-- stream at @key@ ordered by @position@.+newtype EventStoreReader key position m event = EventStoreReader { getEvents :: QueryRange key position -> m [event] } --- | Gets all the events ordered starting with a given 'SequenceNumber', and--- ordered by 'SequenceNumber'. This is used when replaying all the events in a--- store.-newtype GloballyOrderedEventStore serialized m =- GloballyOrderedEventStore- { getSequencedEvents :: EventStoreQueryRange SequenceNumber -> m [GloballyOrderedEvent serialized]- }+instance (Functor m) => Functor (EventStoreReader key position m) where+ fmap f (EventStoreReader reader) = EventStoreReader $ fmap (fmap f) <$> reader +type VersionedEventStoreReader m event = EventStoreReader UUID EventVersion m (VersionedStreamEvent event)+type GlobalEventStoreReader m event = EventStoreReader () SequenceNumber m (GlobalStreamEvent event)++-- | An 'EventStoreWriter' is a function to write some events of type @event@+-- to an event store in some monad @m@.+newtype EventStoreWriter m event = EventStoreWriter { storeEvents :: ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError) }++instance Contravariant (EventStoreWriter m) where+ contramap f (EventStoreWriter writer) = EventStoreWriter $ \vers uuid -> writer vers uuid . fmap f++-- | An event along with the @key@ for the event stream it is from and its+-- @position@ in that event stream.+data StreamEvent key position event+ = StreamEvent+ { streamEventKey :: !key+ , streamEventPosition :: !position+ , streamEventEvent :: !event+ } deriving (Show, Eq, Functor, Foldable, Traversable)++type VersionedStreamEvent event = StreamEvent UUID EventVersion event+type GlobalStreamEvent event = StreamEvent () SequenceNumber (VersionedStreamEvent event)+ -- | ExpectedVersion is used to assert the event stream is at a certain version -- number. This is used when multiple writers are concurrently writing to the -- event store. If the expected version is incorrect, then storing fails.@@ -87,8 +95,8 @@ transactionalExpectedWriteHelper :: (Monad m) => (UUID -> m EventVersion)- -> (UUID -> [serialized] -> m ())- -> ExpectedVersion -> UUID -> [serialized] -> m (Maybe EventWriteError)+ -> (UUID -> [event] -> m ())+ -> ExpectedVersion -> UUID -> [event] -> m (Maybe EventWriteError) transactionalExpectedWriteHelper getLatestVersion' storeEvents' expected = go expected getLatestVersion' storeEvents' where@@ -101,8 +109,8 @@ :: (Monad m) => Maybe (EventVersion -> Bool) -> (UUID -> m EventVersion)- -> (UUID -> [serialized] -> m ())- -> UUID -> [serialized] -> m (Maybe EventWriteError)+ -> (UUID -> [event] -> m ())+ -> UUID -> [event] -> m (Maybe EventWriteError) transactionalExpectedWriteHelper' Nothing _ storeEvents' uuid events = storeEvents' uuid events >> return Nothing transactionalExpectedWriteHelper' (Just f) getLatestVersion' storeEvents' uuid events = do@@ -111,133 +119,62 @@ then storeEvents' uuid events >> return Nothing else return $ Just $ EventStreamNotAtExpectedVersion latestVersion --- | Changes the monad an 'EventStore' runs in. This is useful to run event--- stores in another 'Monad' while forgetting the original 'Monad'.-runEventStoreUsing+-- | Changes the monad an 'EventStoreReader' runs in. This is useful to run+-- event stores in another 'Monad' while forgetting the original 'Monad'.+runEventStoreReaderUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a)- -> EventStore serialized mstore- -> EventStore serialized m-runEventStoreUsing runStore EventStore{..} =- EventStore- { getLatestVersion = runStore . getLatestVersion- , getEvents = \uuid range -> runStore $ getEvents uuid range- , storeEvents = \vers uuid events -> runStore $ storeEvents vers uuid events- }+ -> EventStoreReader key position mstore event+ -> EventStoreReader key position m event+runEventStoreReaderUsing runStore (EventStoreReader f) = EventStoreReader (runStore . f) --- | Analog of 'runEventStoreUsing' for a 'GloballyOrderedEventStore'.-runGloballyOrderedEventStoreUsing+-- | Analog of 'runEventStoreReaderUsing' for a 'EventStoreWriter'.+runEventStoreWriterUsing :: (Monad m, Monad mstore) => (forall a. mstore a -> m a)- -> GloballyOrderedEventStore serialized mstore- -> GloballyOrderedEventStore serialized m-runGloballyOrderedEventStoreUsing runStore GloballyOrderedEventStore{..} =- GloballyOrderedEventStore- { getSequencedEvents = runStore . getSequencedEvents- }+ -> EventStoreWriter mstore event+ -> EventStoreWriter m event+runEventStoreWriterUsing runStore (EventStoreWriter f) =+ EventStoreWriter $ \vers uuid events -> runStore $ f vers uuid events --- | Wraps an 'EventStore' and transparently serializes/deserializes events for--- you. Note that in this implementation deserialization errors when using--- 'getEvents' are simply ignored (the event is not returned).-serializedEventStore+-- | Wraps an 'EventStoreReader' and transparently serializes/deserializes+-- events for you. Note that in this implementation deserialization errors are+-- simply ignored (the event is not returned).+serializedEventStoreReader :: (Monad m) => Serializer event serialized- -> EventStore serialized m- -> EventStore event m-serializedEventStore Serializer{..} store =- EventStore- (getLatestVersion store)- getEvents'- storeEvents'- where- getEvents' uuid mVersion = mapMaybe (traverse deserialize) <$> getEvents store uuid mVersion- storeEvents' expectedVersion uuid events = storeEvents store expectedVersion uuid (serialize <$> events)+ -> EventStoreReader key position m serialized+ -> EventStoreReader key position m event+serializedEventStoreReader Serializer{..} (EventStoreReader reader) =+ EventStoreReader $ fmap (mapMaybe deserialize) . reader --- | Like 'serializedEventStore' except for 'GloballyOrderedEventStore'.-serializedGloballyOrderedEventStore+-- | Convenience wrapper around 'serializedEventStoreReader' for+-- 'VersionedEventStoreReader'.+serializedVersionedEventStoreReader :: (Monad m) => Serializer event serialized- -> GloballyOrderedEventStore serialized m- -> GloballyOrderedEventStore event m-serializedGloballyOrderedEventStore Serializer{..} store =- GloballyOrderedEventStore getSequencedEvents'- where- getSequencedEvents' sequenceNumber =- mapMaybe (traverse deserialize) <$> getSequencedEvents store sequenceNumber---- | A 'ProjectionEvent' is an event that is associated with a 'Projection' via--- the projection's 'UUID'.-data ProjectionEvent event- = ProjectionEvent- { projectionEventProjectionId :: !UUID- -- ^ The UUID of the 'Projection' that the event belongs to.- , projectionEventEvent :: !event- -- ^ The actual event type. Note that this can be a serialized event or the- -- actual Haskell event type.- } deriving (Show, Eq, Functor, Foldable, Traversable)---- | A 'StoredEvent' is an event with associated storage metadata.-data StoredEvent event- = StoredEvent- { storedEventProjectionId :: !UUID- -- ^ The UUID of the 'Projection' that the event belongs to.- , storedEventVersion :: !EventVersion- -- ^ The version of the Projection corresponding to this event.- , storedEventEvent :: !event- -- ^ The actual event type. Note that this can be a serialized event or the- -- actual Haskell event type.- } deriving (Show, Eq, Functor, Foldable, Traversable)--storedEventToProjectionEvent :: StoredEvent event -> ProjectionEvent event-storedEventToProjectionEvent StoredEvent{..} =- ProjectionEvent- { projectionEventProjectionId = storedEventProjectionId- , projectionEventEvent = storedEventEvent- }---- | A 'GloballyOrderedEvent' is like a 'StoredEvent' but has a global--- 'SequenceNumber'.-data GloballyOrderedEvent event- = GloballyOrderedEvent- { globallyOrderedEventProjectionId :: !UUID- -- ^ The UUID of the 'Projection' that the event belongs to.- , globallyOrderedEventVersion :: !EventVersion- -- ^ The version of the Projection corresponding to this event.- , globallyOrderedEventSequenceNumber :: !SequenceNumber- -- ^ The global sequence number of this event.- , globallyOrderedEventEvent :: !event- -- ^ The actual event type. Note that this can be a serialized event or the- -- actual Haskell event type.- } deriving (Show, Eq, Functor, Foldable, Traversable)---- | Extract the 'StoredEvent' from a 'GloballyOrderedEvent'-globallyOrderedEventToStoredEvent :: GloballyOrderedEvent event -> StoredEvent event-globallyOrderedEventToStoredEvent GloballyOrderedEvent{..} =- StoredEvent- { storedEventProjectionId = globallyOrderedEventProjectionId- , storedEventVersion = globallyOrderedEventVersion- , storedEventEvent = globallyOrderedEventEvent- }+ -> VersionedEventStoreReader m serialized+ -> VersionedEventStoreReader m event+serializedVersionedEventStoreReader serializer = serializedEventStoreReader (traverseSerializer serializer) --- | Extract the 'ProjectionEvent' from a 'GloballyOrderedEvent'-globallyOrderedEventToProjectionEvent :: GloballyOrderedEvent event -> ProjectionEvent event-globallyOrderedEventToProjectionEvent GloballyOrderedEvent{..} =- ProjectionEvent- { projectionEventProjectionId = globallyOrderedEventProjectionId- , projectionEventEvent = globallyOrderedEventEvent- }+-- | Convenience wrapper around 'serializedEventStoreReader' for+-- 'GlobalEventStoreReader'.+serializedGlobalEventStoreReader+ :: (Monad m)+ => Serializer event serialized+ -> GlobalEventStoreReader m serialized+ -> GlobalEventStoreReader m event+serializedGlobalEventStoreReader serializer = serializedEventStoreReader (traverseSerializer (traverseSerializer serializer)) --- | Convert a 'StoredEvent' to a 'GloballyOrderedEvent' by adding the--- 'SequenceNumber'. This is mainly used by event stores to create globally--- ordered events.-storedEventToGloballyOrderedEvent :: SequenceNumber -> StoredEvent event -> GloballyOrderedEvent event-storedEventToGloballyOrderedEvent sequenceNumber StoredEvent{..} =- GloballyOrderedEvent- { globallyOrderedEventProjectionId = storedEventProjectionId- , globallyOrderedEventVersion = storedEventVersion- , globallyOrderedEventSequenceNumber = sequenceNumber- , globallyOrderedEventEvent = storedEventEvent- }+-- | Like 'serializedEventStoreReader' but for an 'EventStoreWriter'. Note that+-- 'EventStoreWriter' is an instance of 'Contravariant', so you can just use+-- @contramap serialize@ instead of this function.+serializedEventStoreWriter+ :: (Monad m)+ => Serializer event serialized+ -> EventStoreWriter m serialized+ -> EventStoreWriter m event+serializedEventStoreWriter Serializer{..} = contramap serialize -- | Event versions are a strictly increasing series of integers for each -- projection. They allow us to order the events when they are replayed, and
src/Eventful/Store/Queries.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE TypeFamilies #-} module Eventful.Store.Queries- ( EventStoreQueryRange (..)- , EventStoreQueryStart (..)- , EventStoreQueryLimit (..)+ ( QueryRange (..)+ , QueryStart (..)+ , QueryLimit (..) , allEvents , eventsUntil , eventsStartingAt@@ -11,38 +12,39 @@ , eventsStartingAtTakeLimit ) where --- | This type defines how to query an event stream. It defines both where to--- start and where to stop in the stream.-data EventStoreQueryRange a- = EventStoreQueryRange- { eventStoreQueryRangeStart :: EventStoreQueryStart a- , eventStoreQueryRangeLimit :: EventStoreQueryLimit a- } deriving (Functor)+-- | This type defines how to query an event stream. It defines the stream key+-- and the start/stop points for the query.+data QueryRange key position+ = QueryRange+ { queryRangeKey :: key+ , queryRangeStart :: QueryStart position+ , queryRangeLimit :: QueryLimit position+ } deriving (Show, Eq) -- | This type defines where an event store query starts.-data EventStoreQueryStart a+data QueryStart position = StartFromBeginning- | StartQueryAt a+ | StartQueryAt position deriving (Show, Eq, Functor) -- | This type is used to limit the results of a query from an event store.-data EventStoreQueryLimit a+data QueryLimit position = NoQueryLimit | MaxNumberOfEvents Int- | StopQueryAt a+ | StopQueryAt position deriving (Show, Eq, Functor) -allEvents :: EventStoreQueryRange a-allEvents = EventStoreQueryRange StartFromBeginning NoQueryLimit+allEvents :: key -> QueryRange key position+allEvents key = QueryRange key StartFromBeginning NoQueryLimit -eventsUntil :: a -> EventStoreQueryRange a-eventsUntil end = EventStoreQueryRange StartFromBeginning (StopQueryAt end)+eventsUntil :: key -> position -> QueryRange key position+eventsUntil key end = QueryRange key StartFromBeginning (StopQueryAt end) -eventsStartingAt :: a -> EventStoreQueryRange a-eventsStartingAt start = EventStoreQueryRange (StartQueryAt start) NoQueryLimit+eventsStartingAt :: key -> position -> QueryRange key position+eventsStartingAt key start = QueryRange key (StartQueryAt start) NoQueryLimit -eventsStartingAtUntil :: a -> a -> EventStoreQueryRange a-eventsStartingAtUntil start end = EventStoreQueryRange (StartQueryAt start) (StopQueryAt end)+eventsStartingAtUntil :: key -> position -> position -> QueryRange key position+eventsStartingAtUntil key start end = QueryRange key (StartQueryAt start) (StopQueryAt end) -eventsStartingAtTakeLimit :: a -> Int -> EventStoreQueryRange a-eventsStartingAtTakeLimit start maxNum = EventStoreQueryRange (StartQueryAt start) (MaxNumberOfEvents maxNum)+eventsStartingAtTakeLimit :: key -> position -> Int -> QueryRange key position+eventsStartingAtTakeLimit key start maxNum = QueryRange key (StartQueryAt start) (MaxNumberOfEvents maxNum)
tests/Eventful/SerializerSpec.hs view
@@ -4,6 +4,7 @@ module Eventful.SerializerSpec (spec) where import Data.Dynamic+import Data.Typeable (typeOf) import GHC.Generics import Test.Hspec
− tests/HLint.hs
@@ -1,21 +0,0 @@-module Main (main) where--import Language.Haskell.HLint (hlint)-import System.Exit (exitFailure, exitSuccess)--import Prelude (String, IO, null)--arguments :: [String]-arguments =- [ "src"- , "tests"- , "-i=Redundant do"- , "-i=Unused LANGUAGE pragma" -- This fails on DeriveGeneric- , "-i=Use newtype instead of data"- , "-i=Eta reduce"- ]--main :: IO ()-main = do- hints <- hlint arguments- if null hints then exitSuccess else exitFailure