cqrs 0.6.0 → 0.7.0
raw patch · 8 files changed
+69/−39 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.CQRS.EventStore.Backend: type RawEvent = PersistedEvent ByteString
+ Data.CQRS.GUID: fromByteString :: ByteString -> GUID
+ Data.CQRS.GUID: toByteString :: GUID -> ByteString
+ Data.CQRS.PersistedEvent: PersistedEvent :: !GUID -> e -> !Int -> !Int -> PersistedEvent e
+ Data.CQRS.PersistedEvent: data PersistedEvent e
+ Data.CQRS.PersistedEvent: peAggregateGUID :: PersistedEvent e -> !GUID
+ Data.CQRS.PersistedEvent: peEvent :: PersistedEvent e -> e
+ Data.CQRS.PersistedEvent: peGlobalVer :: PersistedEvent e -> !Int
+ Data.CQRS.PersistedEvent: peSequenceNumber :: PersistedEvent e -> !Int
- Data.CQRS.EventStore: enumerateEventStore :: Serialize e => EventStore e -> Int -> Enumerator (Int, (GUID, Int, e)) IO a
+ Data.CQRS.EventStore: enumerateEventStore :: Serialize e => EventStore e -> Int -> Enumerator (PersistedEvent e) IO a
- Data.CQRS.EventStore.Backend: EventStoreBackend :: (GUID -> Int -> [(ByteString, Int)] -> IO ()) -> (GUID -> Int -> IO (Int, [ByteString])) -> (forall a. Int -> Enumerator (Int, (GUID, Int, ByteString)) IO a) -> (GUID -> (Int, ByteString) -> IO ()) -> (GUID -> IO (Maybe (Int, ByteString))) -> IO Int -> (forall a. IO a -> IO a) -> IO () -> EventStoreBackend
+ Data.CQRS.EventStore.Backend: EventStoreBackend :: (GUID -> Int -> [RawEvent] -> IO ()) -> (GUID -> Int -> IO [RawEvent]) -> (forall a. Int -> Enumerator RawEvent IO a) -> (GUID -> (Int, ByteString) -> IO ()) -> (GUID -> IO (Maybe (Int, ByteString))) -> IO Int -> (forall a. IO a -> IO a) -> IO () -> EventStoreBackend
- Data.CQRS.EventStore.Backend: esbEnumerateAllEvents :: EventStoreBackend -> forall a. Int -> Enumerator (Int, (GUID, Int, ByteString)) IO a
+ Data.CQRS.EventStore.Backend: esbEnumerateAllEvents :: EventStoreBackend -> forall a. Int -> Enumerator RawEvent IO a
- Data.CQRS.EventStore.Backend: esbRetrieveEvents :: EventStoreBackend -> GUID -> Int -> IO (Int, [ByteString])
+ Data.CQRS.EventStore.Backend: esbRetrieveEvents :: EventStoreBackend -> GUID -> Int -> IO [RawEvent]
- Data.CQRS.EventStore.Backend: esbStoreEvents :: EventStoreBackend -> GUID -> Int -> [(ByteString, Int)] -> IO ()
+ Data.CQRS.EventStore.Backend: esbStoreEvents :: EventStoreBackend -> GUID -> Int -> [RawEvent] -> IO ()
Files
- cqrs.cabal +4/−2
- src/Data/CQRS/EventStore/Backend.hs +10/−5
- src/Data/CQRS/GUID.hs +12/−2
- src/Data/CQRS/Internal/AggregateRef.hs +6/−4
- src/Data/CQRS/Internal/EventStore.hs +7/−21
- src/Data/CQRS/Internal/PersistedEvent.hs +18/−0
- src/Data/CQRS/PersistedEvent.hs +5/−0
- src/Data/CQRS/Transaction.hs +7/−5
cqrs.cabal view
@@ -1,5 +1,5 @@ Name: cqrs-Version: 0.6.0+Version: 0.7.0 Synopsis: Command-Query Responsibility Segregation Description: Haskell implementation of the CQRS architectural pattern. License: MIT@@ -16,8 +16,8 @@ , bytestring >= 0.9.0.1 , cereal >= 0.3.3 && < 0.4 , containers >= 0.4- , enumerator >= 0.4.15 && < 0.5 , data-default >= 0.3 && < 0.4+ , enumerator >= 0.4.15 && < 0.5 , random >= 1.0 && < 1.1 , transformers >= 0.2.2 && < 0.3 Extensions: DeriveDataTypeable@@ -37,7 +37,9 @@ Data.CQRS.EventStore Data.CQRS.EventStore.Backend Data.CQRS.GUID+ Data.CQRS.PersistedEvent Data.CQRS.Serialize Data.CQRS.Transaction Other-modules: Data.CQRS.Internal.AggregateRef Data.CQRS.Internal.EventStore+ Data.CQRS.Internal.PersistedEvent
src/Data/CQRS/EventStore/Backend.hs view
@@ -3,12 +3,18 @@ -- event store backend. module Data.CQRS.EventStore.Backend ( EventStoreBackend(..)+ , RawEvent ) where import Data.ByteString (ByteString) import Data.CQRS.GUID+import Data.CQRS.PersistedEvent (PersistedEvent) import Data.Enumerator (Enumerator) +-- | Raw event type. The data associated with an event is not+-- translated in any way.+type RawEvent = PersistedEvent ByteString+ -- | Event stores are the backend used for reading and storing all the -- information about recorded events. data EventStoreBackend =@@ -17,15 +23,14 @@ -- into the event store, starting at the provided version number. -- If the version number does not match the expected value, a -- failure occurs.- esbStoreEvents :: GUID -> Int -> [(ByteString,Int)] -> IO (),+ esbStoreEvents :: GUID -> Int -> [RawEvent] -> IO (), -- | Retrieve the sequence of events associated with the aggregate -- identified by the given GUID. Only events at or after the given -- version number are retrieved. The events are returned in- -- increasing order of version number. The version number of the- -- last event is returned as well.- esbRetrieveEvents :: GUID -> Int -> IO (Int,[ByteString]),+ -- increasing order of version number.+ esbRetrieveEvents :: GUID -> Int -> IO [RawEvent], -- | Enumerate all events later than given logical timestamp.- esbEnumerateAllEvents :: forall a. Int -> Enumerator (Int, (GUID, Int, ByteString)) IO a,+ esbEnumerateAllEvents :: forall a. Int -> Enumerator RawEvent IO a, -- | Write snapshot for aggregate identified by GUID and -- the given version number. The version number is NOT checked -- for validity. If the event store does not support snapshots
src/Data/CQRS/GUID.hs view
@@ -1,9 +1,11 @@ -- | Globally Unique IDentifiers. module Data.CQRS.GUID ( GUID+ , fromByteString , hexDecode , hexEncode , newGUID+ , toByteString ) where import Control.Monad (liftM)@@ -37,8 +39,8 @@ -- | Serialize instance. instance Serialize GUID where- put (GUID s) = put s- get = liftM GUID get+ put = put . toByteString+ get = liftM fromByteString get -- | Hex encode a GUID. hexEncode :: GUID -> ByteString@@ -50,3 +52,11 @@ case B16.decode s of (a,b) | B.length b == 0 -> Just $ GUID a _ -> Nothing++-- | Convert from ByteString.+fromByteString :: ByteString -> GUID+fromByteString = GUID++-- | Convert to ByteString.+toByteString :: GUID -> ByteString+toByteString (GUID g) = g
src/Data/CQRS/Internal/AggregateRef.hs view
@@ -12,8 +12,9 @@ import Control.Monad (liftM) import Control.Monad.IO.Class (MonadIO, liftIO)-import Data.CQRS.GUID (GUID) import Data.CQRS.Eventable (Eventable(..))+import Data.CQRS.GUID (GUID)+import Data.CQRS.PersistedEvent (PersistedEvent(..)) import Data.Foldable (toList) import Data.IORef (IORef, modifyIORef, newIORef, readIORef) import Data.Sequence (Seq, (|>))@@ -23,7 +24,7 @@ -- | Aggregate root reference. data AggregateRef a e = AggregateRef { arValue :: IORef a- , arEvents :: IORef (Seq (e, Int))+ , arEvents :: IORef (Seq (PersistedEvent e)) , arGUID :: GUID , arStartVersion :: Int , arSnapshotVersion :: Int@@ -43,10 +44,11 @@ -- Apply event to aggregate state. modifyIORef (arValue aggregateRef) $ applyEvent event -- Add event to aggregate.- modifyIORef (arEvents aggregateRef) $ \events -> events |> (event,gv)+ modifyIORef (arEvents aggregateRef) $ \events ->+ events |> (PersistedEvent (arGUID aggregateRef) event (arStartVersion aggregateRef + 1 + S.length events) gv) -- | Read aggregate events.-readEvents :: (MonadIO m) => AggregateRef a e -> m [(e,Int)]+readEvents :: (MonadIO m) => AggregateRef a e -> m [PersistedEvent e] readEvents = liftM toList . liftIO . readIORef . arEvents -- | Read aggregate state.
src/Data/CQRS/Internal/EventStore.hs view
@@ -1,7 +1,6 @@ -- | Event store functions. module Data.CQRS.Internal.EventStore ( EventStore- , enumerateAllEvents , enumerateEventStore , getLatestSnapshot , getLatestVersion@@ -17,21 +16,12 @@ import Data.ByteString (ByteString) import Data.CQRS.EventStore.Backend (EventStoreBackend(..)) import Data.CQRS.GUID+import Data.CQRS.Internal.PersistedEvent (PersistedEvent(..), mapPersistedEvent) import Data.CQRS.Serialize (decode') import Data.Enumerator (Enumerator, ($=)) import qualified Data.Enumerator.List as EL import Data.Serialize (Serialize, encode) --- Utilities.-map1st :: (a -> c) -> (a, b) -> (c, b)-map1st f (a,b) = (f a, b)--map2nd :: (b -> c) -> (a,b) -> (a,c)-map2nd f (a,b) = (a, f b)--map3rd :: (c -> d) -> (a,b,c) -> (a,b,d)-map3rd f (a,b,c) = (a,b,f c)- -- Provide a type alias. data EventStore e = EventStore { esBackend :: EventStoreBackend }@@ -42,22 +32,18 @@ -- | Enumerate all the events from an event store that occur at or later -- than a given logical timestamp.-enumerateEventStore :: forall a e . (Serialize e) => EventStore e -> Int -> Enumerator (Int, (GUID, Int, e)) IO a+enumerateEventStore :: forall a e . (Serialize e) => EventStore e -> Int -> Enumerator (PersistedEvent e) IO a enumerateEventStore es minVersion =- esbEnumerateAllEvents (esBackend es) minVersion $= EL.map (\(gv,(g,ev,ed)) -> (gv,(g,ev,decode' ed :: e)))+ esbEnumerateAllEvents (esBackend es) minVersion $= EL.map (mapPersistedEvent decode') withTransaction :: EventStore e -> IO a -> IO a withTransaction (EventStore esb) = esbWithTransaction esb -storeEvents :: Serialize e => EventStore e -> GUID -> Int -> [(e,Int)] -> IO ()-storeEvents (EventStore esb) guid v0 evs = esbStoreEvents esb guid v0 (map (map1st encode) evs)--retrieveEvents :: Serialize e => EventStore e -> GUID -> Int -> IO (Int,[e])-retrieveEvents (EventStore esb) guid v0 = liftM (map2nd $ map decode') $ esbRetrieveEvents esb guid v0+storeEvents :: Serialize e => EventStore e -> GUID -> Int -> [PersistedEvent e] -> IO ()+storeEvents (EventStore esb) guid v0 evs = esbStoreEvents esb guid v0 $ (map $ mapPersistedEvent encode) evs -enumerateAllEvents :: forall a e. Serialize e => EventStore e -> Int -> Enumerator (Int, (GUID, Int, e)) IO a-enumerateAllEvents (EventStore esb) v0 =- esbEnumerateAllEvents esb v0 $= EL.map (map2nd $ map3rd decode')+retrieveEvents :: Serialize e => EventStore e -> GUID -> Int -> IO [PersistedEvent e]+retrieveEvents (EventStore esb) guid v0 = liftM (map $ mapPersistedEvent decode') $ esbRetrieveEvents esb guid v0 writeSnapshot :: EventStore e -> GUID -> (Int, ByteString) -> IO () writeSnapshot (EventStore esb) = esbWriteSnapshot esb
+ src/Data/CQRS/Internal/PersistedEvent.hs view
@@ -0,0 +1,18 @@+module Data.CQRS.Internal.PersistedEvent+ ( PersistedEvent(..)+ , mapPersistedEvent+ ) where++import Data.CQRS.GUID (GUID)++-- | Persisted Event.+data PersistedEvent e =+ PersistedEvent { peAggregateGUID :: !GUID -- ^ GUID of the aggregate.+ , peEvent :: e -- ^ Event.+ , peSequenceNumber :: !Int -- ^ Sequence number within the aggregate.+ , peGlobalVer :: !Int -- ^ Global sequence number.+ }++-- Transform the event data.+mapPersistedEvent :: (e -> e') -> PersistedEvent e -> PersistedEvent e'+mapPersistedEvent f (PersistedEvent g e v gv) = PersistedEvent g (f e) v gv
+ src/Data/CQRS/PersistedEvent.hs view
@@ -0,0 +1,5 @@+module Data.CQRS.PersistedEvent+ ( PersistedEvent(..)+ ) where++import Data.CQRS.Internal.PersistedEvent
src/Data/CQRS/Transaction.hs view
@@ -11,12 +11,13 @@ import Control.Monad.Trans.Class (MonadTrans(..), lift) import Control.Monad.Trans.State (StateT, get, gets, modify, runStateT) import Data.CQRS.Aggregate (Aggregate(..))-import Data.CQRS.Internal.AggregateRef (AggregateRef, mkAggregateRef)-import qualified Data.CQRS.Internal.AggregateRef as AR import Data.CQRS.Eventable (Eventable(..)) import Data.CQRS.EventStore (EventStore)-import qualified Data.CQRS.Internal.EventStore as ES import Data.CQRS.GUID (GUID)+import Data.CQRS.Internal.AggregateRef (AggregateRef, mkAggregateRef)+import qualified Data.CQRS.Internal.AggregateRef as AR+import qualified Data.CQRS.Internal.EventStore as ES+import Data.CQRS.PersistedEvent (PersistedEvent(..)) import Data.Default (Default(..)) import Data.List (find) import Data.Serialize (Serialize)@@ -102,9 +103,10 @@ -- Get latest snapshot (if any). (v0,a0) <- getLatestSnapshot guid -- Get events.- (latestVersion, events) <- lift $ ES.retrieveEvents es guid v0+ events <- lift $ ES.retrieveEvents es guid v0+ let latestVersion = maximum $ (:) v0 (map peSequenceNumber events) -- Build the aggregate state from all the events.- let a = foldr applyEvent a0 events+ let a = foldr (applyEvent . peEvent) a0 events -- Make the aggregate itself (a' :: AggregateRef a e) <- lift $ mkAggregateRef a guid latestVersion v0 -- Add to set of aggregates to commit later.