diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
--- /dev/null
+++ b/LICENSE.md
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -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)
diff --git a/eventium-memory.cabal b/eventium-memory.cabal
new file mode 100644
--- /dev/null
+++ b/eventium-memory.cabal
@@ -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
diff --git a/src/Eventium/ProjectionCache/Memory.hs b/src/Eventium/ProjectionCache/Memory.hs
new file mode 100644
--- /dev/null
+++ b/src/Eventium/ProjectionCache/Memory.hs
@@ -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
diff --git a/src/Eventium/ReadModel/Memory.hs b/src/Eventium/ReadModel/Memory.hs
new file mode 100644
--- /dev/null
+++ b/src/Eventium/ReadModel/Memory.hs
@@ -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'
diff --git a/src/Eventium/Store/Memory.hs b/src/Eventium/Store/Memory.hs
new file mode 100644
--- /dev/null
+++ b/src/Eventium/Store/Memory.hs
@@ -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))
diff --git a/tests/Eventium/ProjectionCache/MemorySpec.hs b/tests/Eventium/ProjectionCache/MemorySpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Eventium/ProjectionCache/MemorySpec.hs
@@ -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
diff --git a/tests/Eventium/Store/MemorySpec.hs b/tests/Eventium/Store/MemorySpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Eventium/Store/MemorySpec.hs
@@ -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
diff --git a/tests/MemoryTestImport.hs b/tests/MemoryTestImport.hs
new file mode 100644
--- /dev/null
+++ b/tests/MemoryTestImport.hs
@@ -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}
diff --git a/tests/Spec.hs b/tests/Spec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
