cqrs-types (empty) → 0.9.0
raw patch · 9 files changed
+253/−0 lines, 9 filesdep +basedep +base16-bytestringdep +base64-bytestringsetup-changed
Dependencies added: base, base16-bytestring, base64-bytestring, bytestring, conduit, deepseq, derive, random
Files
- LICENSE +19/−0
- Setup.lhs +4/−0
- cqrs-types.cabal +37/−0
- src/Data/CQRS/Aggregate.hs +10/−0
- src/Data/CQRS/EventStore/Backend.hs +56/−0
- src/Data/CQRS/Eventable.hs +9/−0
- src/Data/CQRS/GUID.hs +81/−0
- src/Data/CQRS/PersistedEvent.hs +17/−0
- src/Data/CQRS/Serializable.hs +20/−0
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2012 Bardur Arantsson++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ cqrs-types.cabal view
@@ -0,0 +1,37 @@+Name: cqrs-types+Version: 0.9.0+Synopsis: Command-Query Responsibility Segregation. Modules for the basic types.+Description: Haskell implementation of the CQRS architectural pattern.+License: MIT+License-file: LICENSE+Category: Data+Cabal-version: >=1.10+Build-type: Simple+Author: Bardur Arantsson+Maintainer: Bardur Arantsson <bardur@scientician.net>++Library+ Build-Depends: base == 4.*+ , base16-bytestring >= 0.1.1.3 && < 0.2+ , base64-bytestring >= 1 && < 2+ , bytestring >= 0.9.0.1+ , conduit >= 1.0 && < 2+ , deepseq >= 1.3 && < 2+ , derive >= 2.5.11 && < 2.6+ , random >= 1.0 && < 1.1+ Default-language: Haskell2010+ Default-Extensions:+ DeriveDataTypeable+ DeriveFunctor+ FunctionalDependencies+ MultiParamTypeClasses+ Rank2Types+ TemplateHaskell+ ghc-options: -Wall+ hs-source-dirs: src+ Exposed-modules: Data.CQRS.Aggregate+ Data.CQRS.Eventable+ Data.CQRS.EventStore.Backend+ Data.CQRS.GUID+ Data.CQRS.PersistedEvent+ Data.CQRS.Serializable
+ src/Data/CQRS/Aggregate.hs view
@@ -0,0 +1,10 @@+-- | Aggregate type class.+module Data.CQRS.Aggregate+ ( Aggregate+ ) where++import Data.CQRS.Serializable++-- | Type class for aggregates.+class Serializable a => Aggregate a where+ -- No functions at the current time.
+ src/Data/CQRS/EventStore/Backend.hs view
@@ -0,0 +1,56 @@+-- | Event store backend. You only need to import this+-- module if you're planning on implementing a custom+-- event store backend.+module Data.CQRS.EventStore.Backend+ ( EventStoreBackend(..)+ , RawEvent+ , RawSnapshot(..)+ ) where++import Data.ByteString (ByteString)+import Data.Conduit (ResourceT, Source)+import Data.CQRS.GUID+import Data.CQRS.PersistedEvent (PersistedEvent)++-- | Raw event type. The data associated with an event is not+-- translated in any way.+type RawEvent = PersistedEvent ByteString++-- | Raw snapshot.+data RawSnapshot =+ RawSnapshot { rsVersion :: Int+ , rsSnapshotData :: ByteString+ }+ deriving (Eq,Ord,Show)++-- | Event stores are the backend used for reading and storing all the+-- information about recorded events.+class EventStoreBackend esb where+ -- | Store a sequence of events for aggregate identified by GUID+ -- into the event store, starting at the provided version number.+ -- If the version number does not match the expected value, a+ -- failure occurs.+ esbStoreEvents :: esb -> 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.+ esbRetrieveEvents :: esb -> GUID -> Int -> Source (ResourceT IO) RawEvent+ -- | Enumerate all events. There is no guarantee on the ordering+ -- of events /except/ that events for any specific aggregate root+ -- are returned in order of version number.+ esbEnumerateAllEvents :: esb -> Source (ResourceT IO) RawEvent+ -- | 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+ -- this function may do nothing.+ esbWriteSnapshot :: esb -> GUID -> RawSnapshot -> IO ()+ -- | Get latest snapshot of an aggregate identified by GUID.+ -- Returns the version number of the snapshot in addition to the+ -- data. An event store which does not support snapshots is+ -- permitted to return 'Nothing' in all cases.+ esbGetLatestSnapshot :: esb -> GUID -> IO (Maybe RawSnapshot)+ -- | Run transaction against the event store. The transaction is+ -- expected to commit if the supplied IO action runs to completion+ -- (i.e. doesn't throw an exception) and to rollback otherwise.+ esbWithTransaction :: forall a . esb -> IO a -> IO a
+ src/Data/CQRS/Eventable.hs view
@@ -0,0 +1,9 @@+-- | Eventable type class.+module Data.CQRS.Eventable+ ( Eventable(..)+ ) where++-- | Type class for applying events to aggregates.+class Eventable a e | a -> e where+ -- | Apply an event to the aggregate and return the updated aggregate.+ applyEvent :: Maybe a -> e -> Maybe a
+ src/Data/CQRS/GUID.hs view
@@ -0,0 +1,81 @@+-- | Globally Unique IDentifiers.+module Data.CQRS.GUID+ ( GUID+ , fromByteString+ , base64Decode+ , base64Encode+ , hexDecode+ , hexEncode+ , newGUID+ , toByteString+ ) where++import Control.DeepSeq (NFData(..))+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Base64 as B64+import Data.ByteString.Char8 ()+import Data.Data (Data)+import Data.DeriveTH (derive, makeNFData)+import Data.Typeable (Typeable)+import Data.Word (Word8)+import System.Random (randomRIO)++import Data.CQRS.Serializable++-- | A Globally Unique IDentifier.+newtype GUID = GUID ByteString+ deriving (Typeable, Eq, Ord, Data)++-- | Get a random byte.+randomWord8 :: IO Word8+randomWord8 = fmap fromInteger $ randomRIO (0,255)++-- | Create a new random GUID.+newGUID :: IO GUID+newGUID = do+ uuid <- sequence $ replicate 16 randomWord8+ return $ GUID $ B.pack uuid++-- | Serialize instance.+instance Serializable GUID where+ serialize = toByteString+ deserialize = Just . GUID++-- | Hex encode a GUID.+hexEncode :: GUID -> ByteString+hexEncode (GUID s) = B16.encode s++-- | Base64 encode a GUID.+base64Encode :: GUID -> ByteString+base64Encode (GUID s) = B64.encode s++-- | Showing a GUID.+instance Show GUID where+ show = show . base64Encode++-- | Decode a GUID from hex representation.+hexDecode :: ByteString -> Maybe GUID+hexDecode s =+ case B16.decode s of+ (a,b) | B.length b == 0 -> Just $ GUID a+ _ -> Nothing++-- | Decode a GUID from base64 representation.+base64Decode :: ByteString -> Maybe GUID+base64Decode s =+ case B64.decode s of+ Right a -> Just $ GUID a+ Left _ -> Nothing++-- | Convert from ByteString.+fromByteString :: ByteString -> GUID+fromByteString = GUID++-- | Convert to ByteString.+toByteString :: GUID -> ByteString+toByteString (GUID g) = g++-- Instances+$(derive makeNFData ''GUID)
+ src/Data/CQRS/PersistedEvent.hs view
@@ -0,0 +1,17 @@+module Data.CQRS.PersistedEvent+ ( PersistedEvent(..)+ ) where++import Control.DeepSeq (NFData(..))+import Data.CQRS.GUID (GUID)+import Data.DeriveTH (derive, makeNFData)++-- | Persisted Event.+data PersistedEvent e =+ PersistedEvent { peAggregateGUID :: !GUID -- ^ GUID of the aggregate.+ , peEvent :: !e -- ^ Event.+ , peSequenceNumber :: !Int -- ^ Sequence number within the aggregate.+ }+ deriving (Show, Eq, Ord, Functor)++$(derive makeNFData ''PersistedEvent)
+ src/Data/CQRS/Serializable.hs view
@@ -0,0 +1,20 @@+-- | Serialization support. This is mainly used for compatibility+-- with whatever serialization library you want to use.++module Data.CQRS.Serializable+ ( Serializable(..)+ ) where++import Data.ByteString (ByteString)++-- | Serialization support for values of type 'a'.+class Serializable a where+ -- | Serialize a value. The serialized representation+ -- should contain some metadata (a UUID for example)+ -- which can be used to check reliably whether the encoded+ -- representation is semantically valid upon decoding.+ serialize :: a -> ByteString+ -- | De-serialize a value from a byte string. Should return+ -- 'Nothing' if decoding is not possible due to a now-invalid+ -- representation.+ deserialize :: ByteString -> Maybe a