packages feed

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 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 #-}