packages feed

cqrs-0.9.0: src/Data/CQRS/Internal/EventBus.hs

module Data.CQRS.Internal.EventBus
    ( EventBus(..)
    , Subscription(..)
    , newEventBus
    ) where

-- External imports
import           Control.Concurrent.STM (atomically)
import qualified Control.Concurrent.STM.TChan as C
import           Control.DeepSeq (NFData, ($!!))
import           Control.Monad.IO.Class (MonadIO, liftIO)

-- Internal imports
import           Data.CQRS.PersistedEvent

-- | Event bus that broadcasts events of a certain type.
data EventBus e = EventBus
    { publishEventsToBus :: [PersistedEvent e] -> IO ()
    , subscribeToEventBus :: IO (Subscription e)
    , unsubscribeFromEventBus :: Subscription e -> IO ()
    }

-- | A subscription to an event bus.
newtype Subscription e = Subscription
    { readEventFromSubscription :: IO [PersistedEvent e]
    }

-- | Create a new event bus.
newEventBus :: (MonadIO m, Show e, NFData e) => m (EventBus e)
newEventBus = do
  c <- liftIO $ atomically $ C.newBroadcastTChan
  let publish events = do
        atomically $ C.writeTChan c $!! events
      subscribe = do
        c' <- atomically $ C.dupTChan c
        return $ Subscription (atomically $ C.readTChan c')
      unsubscribe _ = do
        -- TODO: Should we set a flag to detect post-unsubscribe use?
        return () -- Needs no cleanup.
  return $ EventBus publish subscribe unsubscribe