eventium-memory (empty) → 0.1.0
raw patch · 11 files changed
+639/−0 lines, 11 filesdep +HUnitdep +basedep +containers
Dependencies added: HUnit, base, containers, eventium-core, eventium-test-helpers, hspec, mtl, safe, stm
Files
- CHANGELOG.md +0/−0
- LICENSE.md +20/−0
- README.md +122/−0
- eventium-memory.cabal +76/−0
- src/Eventium/ProjectionCache/Memory.hs +60/−0
- src/Eventium/ReadModel/Memory.hs +35/−0
- src/Eventium/Store/Memory.hs +162/−0
- tests/Eventium/ProjectionCache/MemorySpec.hs +52/−0
- tests/Eventium/Store/MemorySpec.hs +75/−0
- tests/MemoryTestImport.hs +36/−0
- tests/Spec.hs +1/−0
+ CHANGELOG.md view
+ LICENSE.md view
@@ -0,0 +1,20 @@+Copyright (c) 2025 Alexander Sidorenko+Copyright (c) 2016-2017 David Reaver++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.
+ README.md view
@@ -0,0 +1,122 @@+# Eventium Memory++In-memory implementations of event stores, read models, and projection caches for Eventium.++## Overview++`eventium-memory` provides thread-safe, in-memory storage implementations for the Eventium event sourcing framework. This package is ideal for development, testing, and prototyping event-sourced applications without requiring external dependencies like databases.++## Features++- ✅ **In-Memory Event Store** - Fast, STM-based event storage+- ✅ **Thread-Safe** - Concurrent access via Software Transactional Memory+- ✅ **No External Dependencies** - No database setup required+- ✅ **Perfect for Testing** - Fast test execution with isolated state+- ✅ **Projection Cache** - In-memory snapshot storage+- ✅ **Read Model Support** - Memory-based read model implementation++## Components++### Event Store (`Eventium.Store.Memory`)+Implements both `EventStoreReader` and `EventStoreWriter` with:+- Per-stream versioning+- Global event ordering+- Optimistic concurrency control+- STM transactions for atomicity++### Read Model (`Eventium.ReadModel.Memory`)+In-memory read model implementation for building query-optimized views.++### Projection Cache (`Eventium.ProjectionCache.Memory`)+Stores projection snapshots in memory to avoid replaying entire event histories.++## Usage++```haskell+import Eventium.Store.Memory (newEventStore)+import Control.Concurrent.STM (atomically)++main :: IO ()+main = do+ -- Create a new in-memory event store+ store <- atomically newEventStore+ + -- Use it with your command handlers and projections+ result <- applyCommandHandler + (eventStoreWriter store)+ (eventStoreReader store) + myCommandHandler + aggregateId + command+```++## Installation++Add to your `package.yaml`:++```yaml+dependencies:+ - eventium-core+ - eventium-memory+```++Or to your `.cabal` file:++```cabal+build-depends:+ eventium-core+ , eventium-memory+```++## Use Cases++### Development+Quickly prototype event-sourced applications without database setup.++### Testing+- Fast test execution (no I/O overhead)+- Isolated test state (each test gets fresh store)+- Easy verification of event sequences++### Demonstration+Perfect for demos, tutorials, and learning event sourcing concepts.++## Example++```haskell+-- Create store+store <- atomically newEventStore++-- Write events+writeResult <- atomically $ + writeEvents (eventStoreWriter store) + streamKey + ExpectedPositionAny + [event1, event2]++-- Read events back+events <- atomically $ + readEvents (eventStoreReader store) + streamKey + QueryRangeAll+```++## Limitations++- **Not Persistent** - All data lost when process ends+- **Memory Constraints** - Limited by available RAM+- **Single Process** - No distributed access++For production use, consider:+- `eventium-sqlite` - Persistent, single-process storage+- `eventium-postgresql` - Persistent, multi-process storage++## Documentation++- [Main README](../README.md) - Project overview+- [Design Documentation](../DESIGN.md) - Architecture details+- [Examples](../examples/) - Working applications++## License++MIT - see [LICENSE.md](LICENSE.md)
+ eventium-memory.cabal view
@@ -0,0 +1,76 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.37.0.+--+-- see: https://github.com/sol/hpack++name: eventium-memory+version: 0.1.0+synopsis: In-memory implementations for eventium+description: Eventium-memory provides in-memory implementations of event stores, read models, and projection caches+ for the Eventium event sourcing framework. This package is ideal for development, testing, and prototyping+ event-sourced applications without requiring external dependencies like databases. All data is stored in+ STM-based concurrent data structures for thread-safe access.+category: Database,Eventsourcing+stability: experimental+homepage: https://github.com/aleks-sidorenko/eventium#readme+bug-reports: https://github.com/aleks-sidorenko/eventium/issues+maintainer: Alexander Sidorenko+license: MIT+license-file: LICENSE.md+build-type: Simple+extra-source-files:+ CHANGELOG.md+ README.md++source-repository head+ type: git+ location: https://github.com/aleks-sidorenko/eventium++library+ exposed-modules:+ Eventium.ProjectionCache.Memory+ Eventium.ReadModel.Memory+ Eventium.Store.Memory+ other-modules:+ Paths_eventium_memory+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ base >=4.9 && <5+ , containers >=0.6 && <0.8+ , eventium-core+ , mtl >=2.2 && <2.4+ , safe >=0.3 && <0.4+ , stm >=2.5 && <2.6+ default-language: Haskell2010++test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Eventium.ProjectionCache.MemorySpec+ Eventium.Store.MemorySpec+ MemoryTestImport+ Eventium.ProjectionCache.Memory+ Eventium.ReadModel.Memory+ Eventium.Store.Memory+ Paths_eventium_memory+ hs-source-dirs:+ tests+ src+ ghc-options: -Wall+ build-tool-depends:+ hspec-discover:hspec-discover+ build-depends:+ HUnit+ , base >=4.9 && <5+ , containers >=0.6 && <0.8+ , eventium-core+ , eventium-test-helpers+ , hspec+ , mtl >=2.2 && <2.4+ , safe >=0.3 && <0.4+ , stm >=2.5 && <2.6+ default-language: Haskell2010
+ src/Eventium/ProjectionCache/Memory.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-}++module Eventium.ProjectionCache.Memory+ ( ProjectionMap,+ emptyProjectionMap,+ projectionMapTVar,+ tvarProjectionCache,+ embeddedStateProjectionCache,+ module Eventium.ProjectionCache.Types,+ )+where++import Control.Concurrent.STM+import Control.Monad.State.Class hiding (state)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Eventium.ProjectionCache.Types++-- | A 'ProjectionMap' just stores the latest snapshot for each UUID.+type ProjectionMap key position serialized = Map key (position, serialized)++emptyProjectionMap :: ProjectionMap key position serialized+emptyProjectionMap = Map.empty++projectionMapTVar :: IO (TVar (ProjectionMap key position serialized))+projectionMapTVar = newTVarIO emptyProjectionMap++storeProjectionInMap ::+ (Ord key) =>+ key ->+ position ->+ serialized ->+ ProjectionMap key position serialized ->+ ProjectionMap key position serialized+storeProjectionInMap uuid version state = Map.insert uuid (version, state)++-- | A 'ProjectionCache' that uses a 'TVar' and runs in 'STM'.+tvarProjectionCache ::+ (Ord key) =>+ TVar (ProjectionMap key position serialized) ->+ ProjectionCache key position serialized STM+tvarProjectionCache tvar =+ let storeProjectionSnapshot uuid version projState = modifyTVar' tvar (storeProjectionInMap uuid version projState)+ loadProjectionSnapshot uuid = Map.lookup uuid <$> readTVar tvar+ in ProjectionCache {..}++-- | A 'ProjectionCache' for some 'MonadState' that contains a 'ProjectionMap'.+embeddedStateProjectionCache ::+ (MonadState s m, Ord key) =>+ (s -> ProjectionMap key position serialized) ->+ (s -> ProjectionMap key position serialized -> s) ->+ ProjectionCache key position serialized m+embeddedStateProjectionCache getMap setMap =+ let storeProjectionSnapshot uuid version projState = modify' (storeProjectionSnapshot' uuid version projState)+ loadProjectionSnapshot uuid = Map.lookup uuid <$> gets getMap+ in ProjectionCache {..}+ where+ storeProjectionSnapshot' uuid version projState state =+ setMap state $ storeProjectionInMap uuid version projState $ getMap state
+ src/Eventium/ReadModel/Memory.hs view
@@ -0,0 +1,35 @@+module Eventium.ReadModel.Memory+ ( memoryReadModel,+ )+where++import Control.Concurrent.STM+import Control.Monad.IO.Class+import Eventium.ReadModel.Class+import Eventium.Store.Class+import Safe (maximumDef)++data MemoryReadModelData modeldata+ = MemoryReadModelData+ { memoryReadModelDataLatestSequenceNumber :: SequenceNumber,+ _memoryReadModelDataValue :: modeldata+ }+ deriving (Show)++-- | Creates a read model that wraps some pure data in a TVar and manages the+-- latest sequence number for you.+memoryReadModel ::+ (MonadIO m) =>+ modeldata ->+ (modeldata -> [GlobalStreamEvent serialized] -> m modeldata) ->+ IO (ReadModel (TVar (MemoryReadModelData modeldata)) serialized m)+memoryReadModel initialValue handleEvents = do+ tvar <- newTVarIO $ MemoryReadModelData (-1) initialValue+ return $ ReadModel tvar getLatestSequence handleTVarEvents+ where+ getLatestSequence tvar' = liftIO $ memoryReadModelDataLatestSequenceNumber <$> readTVarIO tvar'+ handleTVarEvents tvar' events = do+ (MemoryReadModelData latestSeq modelData) <- liftIO $ readTVarIO tvar'+ let latestSeq' = maximumDef latestSeq (streamEventPosition <$> events)+ modelData' <- handleEvents modelData events+ liftIO . atomically . writeTVar tvar' $ MemoryReadModelData latestSeq' modelData'
+ src/Eventium/Store/Memory.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE FlexibleContexts #-}++module Eventium.Store.Memory+ ( tvarEventStoreReader,+ tvarEventStoreWriter,+ tvarGlobalEventStoreReader,+ stateEventStoreReader,+ stateEventStoreWriter,+ stateGlobalEventStoreReader,+ embeddedStateEventStoreReader,+ embeddedStateEventStoreWriter,+ embeddedStateGlobalEventStoreReader,+ EventMap,+ emptyEventMap,+ eventMapTVar,+ module Eventium.Store.Class,+ )+where++import Control.Concurrent.STM+import Control.Monad.State.Class+import Data.Foldable (toList)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe (fromMaybe)+import Data.Sequence (Seq, (><))+import qualified Data.Sequence as Seq+import Eventium.Store.Class+import Eventium.UUID++-- | Internal data structure used for the in-memory event stores.+data EventMap event+ = EventMap+ { _eventMapUuidMap :: Map UUID (Seq (VersionedStreamEvent event)),+ _eventMapGlobalEvents :: Seq (VersionedStreamEvent event)+ }+ deriving (Show)++-- | What it says on the tin, an initialized empty 'EventMap'+emptyEventMap :: EventMap event+emptyEventMap = EventMap Map.empty Seq.empty++-- | Initialize an 'EventMap' in a 'TVar'+eventMapTVar :: IO (TVar (EventMap event))+eventMapTVar = newTVarIO emptyEventMap++-- | An 'EventStoreReader' that stores events in a 'TVar' and runs in 'STM'.+-- This functions initializes the store by creating the 'TVar' and hooking up+-- the event store API to that 'TVar'.+tvarEventStoreReader :: TVar (EventMap event) -> VersionedEventStoreReader STM event+tvarEventStoreReader tvar = EventStoreReader $ \range -> lookupEventsInRange range <$> readTVar tvar++tvarEventStoreWriter :: TVar (EventMap event) -> VersionedEventStoreWriter STM event+tvarEventStoreWriter tvar = EventStoreWriter $ transactionalExpectedWriteHelper getLatestVersion storeEvents'+ where+ getLatestVersion uuid = flip latestEventVersion uuid <$> readTVar tvar+ storeEvents' uuid events = do+ store <- readTVar tvar+ let (store', vers) = storeEventMap store uuid events+ writeTVar tvar store'+ return vers++-- | Analog of 'tvarEventStoreReader' for a 'GlobalEventStoreReader'+tvarGlobalEventStoreReader :: TVar (EventMap event) -> GlobalEventStoreReader STM event+tvarGlobalEventStoreReader tvar = EventStoreReader $ \range -> lookupGlobalEvents range <$> readTVar tvar++-- | Specialized version of 'embeddedStateEventStoreReader' that only contains an+-- 'EventMap' in the state.+stateEventStoreReader ::+ (MonadState (EventMap event) m) =>+ VersionedEventStoreReader m event+stateEventStoreReader = embeddedStateEventStoreReader id++stateGlobalEventStoreReader ::+ (MonadState (EventMap event) m) =>+ GlobalEventStoreReader m event+stateGlobalEventStoreReader = embeddedStateGlobalEventStoreReader id++-- | Specialized version of 'embeddedStateEventStoreWriter' that only contains an+-- 'EventMap' in the state.+stateEventStoreWriter ::+ (MonadState (EventMap event) m) =>+ VersionedEventStoreWriter m event+stateEventStoreWriter = embeddedStateEventStoreWriter id (const id)++-- | An 'EventStore' that runs on some 'MonadState' that contains an+-- 'EventMap'. This is useful if you want to include other state in your+-- 'MonadState'.+embeddedStateEventStoreReader ::+ (MonadState s m) =>+ (s -> EventMap event) ->+ VersionedEventStoreReader m event+embeddedStateEventStoreReader getMap = EventStoreReader $ \range -> lookupEventsInRange range <$> gets getMap++embeddedStateEventStoreWriter ::+ (MonadState s m) =>+ (s -> EventMap event) ->+ (s -> EventMap event -> s) ->+ VersionedEventStoreWriter m event+embeddedStateEventStoreWriter getMap setMap = EventStoreWriter $ transactionalExpectedWriteHelper getLatestVersion storeEvents'+ where+ getLatestVersion uuid = flip latestEventVersion uuid <$> gets getMap+ storeEvents' uuid events = do+ state' <- get+ let store = getMap state'+ let (store', vers) = storeEventMap store uuid events+ put $ setMap state' store'+ return vers++-- | Analogous to 'embeddedStateEventStore' for a 'GlobalStreamEventStore'.+embeddedStateGlobalEventStoreReader ::+ (MonadState s m) =>+ (s -> EventMap event) ->+ GlobalEventStoreReader m event+embeddedStateGlobalEventStoreReader getMap = EventStoreReader $ \range -> lookupGlobalEvents range <$> gets getMap++lookupEventMapRaw :: EventMap event -> UUID -> Seq (VersionedStreamEvent event)+lookupEventMapRaw (EventMap uuidMap _) uuid = fromMaybe Seq.empty $ Map.lookup uuid uuidMap++lookupEventsInRange :: QueryRange UUID EventVersion -> EventMap event -> [VersionedStreamEvent event]+lookupEventsInRange (QueryRange uuid start limit) store = toList $ filterEventsByRange start' limit' 0 rawEvents+ where+ start' = unEventVersion <$> start+ limit' = unEventVersion <$> limit+ rawEvents = lookupEventMapRaw store uuid++filterEventsByRange :: QueryStart Int -> QueryLimit Int -> Int -> Seq event -> Seq event+filterEventsByRange queryStart queryLimit defaultStart events =+ let (start', events') =+ case queryStart of+ StartFromBeginning -> (defaultStart, events)+ StartQueryAt start -> (start, Seq.drop (start - defaultStart) events)+ events'' =+ case queryLimit of+ NoQueryLimit -> events'+ MaxNumberOfEvents num -> Seq.take num events'+ StopQueryAt stop -> Seq.take (stop - start' + 1) events'+ in events''++latestEventVersion :: EventMap event -> UUID -> EventVersion+latestEventVersion store uuid = EventVersion $ Seq.length (lookupEventMapRaw store uuid) - 1++lookupGlobalEvents :: QueryRange () SequenceNumber -> EventMap event -> [GlobalStreamEvent event]+lookupGlobalEvents (QueryRange () start limit) (EventMap _ globalEvents) = events'+ where+ start' = unSequenceNumber <$> start+ limit' = unSequenceNumber <$> limit+ events = toList $ filterEventsByRange start' limit' 1 globalEvents+ events' = zipWith (StreamEvent ()) [startingSeqNum ..] events+ startingSeqNum =+ case start of+ StartFromBeginning -> 1+ (StartQueryAt startSeq) -> startSeq++storeEventMap ::+ EventMap event -> UUID -> [event] -> (EventMap event, EventVersion)+storeEventMap store@(EventMap uuidMap globalEvents) uuid events =+ let versStart = latestEventVersion store uuid+ streamEvents = zipWith (StreamEvent uuid) [versStart + 1 ..] events+ newMap = Map.insertWith (flip (><)) uuid (Seq.fromList streamEvents) uuidMap+ globalEvents' = globalEvents >< Seq.fromList streamEvents+ in (EventMap newMap globalEvents', versStart + EventVersion (length events))
+ tests/Eventium/ProjectionCache/MemorySpec.hs view
@@ -0,0 +1,52 @@+module Eventium.ProjectionCache.MemorySpec (spec) where++import Control.Concurrent.STM+import Control.Monad.State.Strict+import Eventium.ProjectionCache.Memory+import Eventium.Store.Memory+import Eventium.TestHelpers+import MemoryTestImport+import Test.Hspec++spec :: Spec+spec = do+ describe "TVar projection cache" $ do+ versionedProjectionCacheSpec tvarVersionedProjectionCacheRunner+ globalStreamProjectionCacheSpec tvarGlobalStreamProjectionCacheRunner++ describe "MonadState embedded memory projection cache" $ do+ versionedProjectionCacheSpec stateVersionedProjectionCacheRunner+ globalStreamProjectionCacheSpec stateGlobalStreamProjectionCacheRunner++tvarVersionedProjectionCacheRunner :: VersionedProjectionCacheRunner STM+tvarVersionedProjectionCacheRunner = VersionedProjectionCacheRunner $ \action -> do+ eventTVar <- eventMapTVar+ projTVar <- projectionMapTVar+ let writer = tvarEventStoreWriter eventTVar+ reader = tvarEventStoreReader eventTVar+ cache = tvarProjectionCache projTVar+ atomically $ action writer reader cache++tvarGlobalStreamProjectionCacheRunner :: GlobalStreamProjectionCacheRunner STM+tvarGlobalStreamProjectionCacheRunner = GlobalStreamProjectionCacheRunner $ \action -> do+ eventTVar <- eventMapTVar+ projTVar <- projectionMapTVar+ let writer = tvarEventStoreWriter eventTVar+ globalReader = tvarGlobalEventStoreReader eventTVar+ cache = tvarProjectionCache projTVar+ atomically $ action writer globalReader cache++stateVersionedProjectionCacheRunner :: VersionedProjectionCacheRunner (StateT (StreamEmbeddedState Counter CounterEvent) IO)+stateVersionedProjectionCacheRunner = VersionedProjectionCacheRunner $ \action -> evalStateT (action writer reader cache) emptyEmbeddedState+ where+ writer = embeddedStateEventStoreWriter embeddedEventMap setEventMap+ reader = embeddedStateEventStoreReader embeddedEventMap+ cache = embeddedStateProjectionCache embeddedProjectionMap setProjectionMap++stateGlobalStreamProjectionCacheRunner :: GlobalStreamProjectionCacheRunner (StateT (GlobalStreamEmbeddedState Counter CounterEvent Text) IO)+stateGlobalStreamProjectionCacheRunner =+ GlobalStreamProjectionCacheRunner $ \action -> evalStateT (action writer globalReader cache) emptyEmbeddedState+ where+ writer = embeddedStateEventStoreWriter embeddedEventMap setEventMap+ globalReader = embeddedStateGlobalEventStoreReader embeddedEventMap+ cache = embeddedStateProjectionCache embeddedProjectionMap setProjectionMap
+ tests/Eventium/Store/MemorySpec.hs view
@@ -0,0 +1,75 @@+module Eventium.Store.MemorySpec (spec) where++import Control.Concurrent.STM+import Control.Monad.State.Strict+import Eventium.Serializer+import Eventium.Store.Memory+import Eventium.TestHelpers+import MemoryTestImport+import Test.Hspec++spec :: Spec+spec = do+ describe "TVar memory event store with actual event type" $ do+ eventStoreSpec tvarRunner+ globalStreamEventStoreSpec tvarGlobalRunner++ describe "TVar memory event store with Dynamic serialized type" $ do+ eventStoreSpec tvarDynamicRunner+ globalStreamEventStoreSpec tvarDynamicGlobalRunner++ describe "MonadState memory event store with actual event type" $ do+ eventStoreSpec stateStoreRunner+ globalStreamEventStoreSpec stateStoreGlobalRunner++ describe "MonadState embedded memory event store with actual event type" $ do+ eventStoreSpec embeddedStateStoreRunner+ globalStreamEventStoreSpec embeddedStateStoreGlobalRunner++tvarRunner :: EventStoreRunner STM+tvarRunner = EventStoreRunner $ \action -> do+ eventTVar <- eventMapTVar+ let writer = tvarEventStoreWriter eventTVar+ reader = tvarEventStoreReader eventTVar+ atomically $ action writer reader++tvarGlobalRunner :: GlobalStreamEventStoreRunner STM+tvarGlobalRunner = GlobalStreamEventStoreRunner $ \action -> do+ eventTVar <- eventMapTVar+ let writer = tvarEventStoreWriter eventTVar+ globalReader = tvarGlobalEventStoreReader eventTVar+ atomically $ action writer globalReader++tvarDynamicRunner :: EventStoreRunner STM+tvarDynamicRunner = EventStoreRunner $ \action -> do+ eventTVar <- eventMapTVar+ let writer = serializedEventStoreWriter dynamicSerializer $ tvarEventStoreWriter eventTVar+ reader = serializedVersionedEventStoreReader dynamicSerializer $ tvarEventStoreReader eventTVar+ atomically $ action writer reader++tvarDynamicGlobalRunner :: GlobalStreamEventStoreRunner STM+tvarDynamicGlobalRunner = GlobalStreamEventStoreRunner $ \action -> do+ eventTVar <- eventMapTVar+ let writer = serializedEventStoreWriter dynamicSerializer $ tvarEventStoreWriter eventTVar+ globalReader = serializedGlobalEventStoreReader dynamicSerializer $ tvarGlobalEventStoreReader eventTVar+ atomically $ action writer globalReader++stateStoreRunner :: EventStoreRunner (StateT (EventMap CounterEvent) IO)+stateStoreRunner = EventStoreRunner $ \action -> evalStateT (action stateEventStoreWriter stateEventStoreReader) emptyEventMap++stateStoreGlobalRunner :: GlobalStreamEventStoreRunner (StateT (EventMap CounterEvent) IO)+stateStoreGlobalRunner = GlobalStreamEventStoreRunner $+ \action -> evalStateT (action stateEventStoreWriter stateGlobalEventStoreReader) emptyEventMap++embeddedStateStoreRunner :: EventStoreRunner (StateT (StreamEmbeddedState Counter CounterEvent) IO)+embeddedStateStoreRunner = EventStoreRunner $ \action -> evalStateT (action writer reader) emptyEmbeddedState+ where+ writer = embeddedStateEventStoreWriter embeddedEventMap setEventMap+ reader = embeddedStateEventStoreReader embeddedEventMap++embeddedStateStoreGlobalRunner :: GlobalStreamEventStoreRunner (StateT (StreamEmbeddedState Counter CounterEvent) IO)+embeddedStateStoreGlobalRunner = GlobalStreamEventStoreRunner $+ \action -> evalStateT (action writer globalReader) emptyEmbeddedState+ where+ writer = embeddedStateEventStoreWriter embeddedEventMap setEventMap+ globalReader = embeddedStateGlobalEventStoreReader embeddedEventMap
+ tests/MemoryTestImport.hs view
@@ -0,0 +1,36 @@+module MemoryTestImport+ ( EmbeddedState (..),+ StreamEmbeddedState,+ GlobalStreamEmbeddedState,+ emptyEmbeddedState,+ setEventMap,+ setProjectionMap,+ )+where++import Eventium.ProjectionCache.Memory+import Eventium.Store.Memory+import Eventium.UUID++data EmbeddedState state event key position+ = EmbeddedState+ { _embeddedDummyArgument :: Int,+ embeddedEventMap :: EventMap event,+ embeddedProjectionMap :: ProjectionMap key position state+ }++type StreamEmbeddedState state event = EmbeddedState state event UUID EventVersion++type GlobalStreamEmbeddedState state event key = EmbeddedState state event key SequenceNumber++emptyEmbeddedState :: EmbeddedState state event key position+emptyEmbeddedState = EmbeddedState 100 emptyEventMap emptyProjectionMap++setEventMap :: EmbeddedState state event key position -> EventMap event -> EmbeddedState state event key position+setEventMap state' eventMap = state' {embeddedEventMap = eventMap}++setProjectionMap ::+ EmbeddedState state event key position ->+ ProjectionMap key position state ->+ EmbeddedState state event key position+setProjectionMap state' projectionMap = state' {embeddedProjectionMap = projectionMap}
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}