packages feed

domaindriven-core-0.5.0: src/DomainDriven/Persistance/ForgetfulInMemory.hs

module DomainDriven.Persistance.ForgetfulInMemory where

import Data.List (foldl')
import DomainDriven.Persistance.Class
import GHC.Generics (Generic)
import qualified Streamly.Prelude as S
import UnliftIO
import Prelude

createForgetful
    :: MonadIO m
    => (model -> Stored event -> model)
    -> model
    -- ^ initial model
    -> m (ForgetfulInMemory model event)
createForgetful appEvent m0 = do
    state <- newIORef m0
    evs <- newIORef []
    lock <- newQSem 1
    pure $ ForgetfulInMemory state appEvent m0 evs lock

-- | STM state without event persistance
data ForgetfulInMemory model event = ForgetfulInMemory
    { stateRef :: IORef model
    , apply :: model -> Stored event -> model
    , seed :: model
    , events :: IORef [Stored event]
    , lock :: QSem
    }
    deriving (Generic)

instance ReadModel (ForgetfulInMemory model e) where
    type Model (ForgetfulInMemory model e) = model
    type Event (ForgetfulInMemory model e) = e
    applyEvent ff = apply ff
    getModel ff = readIORef $ stateRef ff
    getEventList ff = readIORef $ events ff
    getEventStream ff = do
        l <- liftIO $ getEventList ff
        S.fromList l

instance WriteModel (ForgetfulInMemory model e) where
    transactionalUpdate ff evalCmd =
        bracket_ (waitQSem $ lock ff) (signalQSem $ lock ff) $ do
            model <- readIORef $ stateRef ff
            (returnFun, evs) <- evalCmd model
            storedEvs <- traverse toStored evs
            let newModel = foldl' (apply ff) model storedEvs
            modifyIORef (events ff) (<> storedEvs)
            writeIORef (stateRef ff) newModel
            pure $ returnFun newModel