eDSL - реализовать действие, которое можно использовать только один раз в последовательности - PullRequest
0 голосов
/ 19 ноября 2018

Я разрабатываю следующий eDSL:

import Control.Monad.Free

type CommandHandler
      = PersistedCommand -> Maybe AggregateSnapshot -> CommandDirective

data CommandDirective = Reject RejectionReason
                      | SkipBecauseAlreadyProcessed
                      | Transact (CommandTransaction ())

type RejectionReason = String

data Action a = PersistEvent Event a
              | UpdateSnapshot AggregateSnapshot a
              | GetCurrentTime (UTCTime -> a )
              | GetNewEventId (EventId -> a) deriving (Functor)

type CommandTransaction a = Free Action a

persistEvent :: Event -> CommandTransaction ()
persistEvent event = Free . PersistEvent event $ Pure ()

updateSnapshot :: AggregateSnapshot -> CommandTransaction ()
updateSnapshot aggregateSnapshot
        = Free . UpdateSnapshot aggregateSnapshot $ Pure ()

getNewEventID :: CommandTransaction EventId
getNewEventID = Free $ GetNewEventId Pure

getCurrentTime :: CommandTransaction UTCTime
getCurrentTime = Free $ GetCurrentTime Pure

Я бы хотел, чтобы UpdateSnapshot использовался только один раз в последовательности, а не дважды, как я делаю сейчас. Я мог бы сделать это с фантомными типами, и тогда UpdateSnapshot будет последним элементом цепочки действий. Но это отчасти семантически неправильно и не работает для двух действий с одинаковой природой тогда ...

Например: 2 последнее обновлениеSnapshot не должно быть действительным:

do
 now <- getCurrentTime
 eventId <- getNewEventID
 persistEvent $ WorkspaceCreated
                 { eventId = eventId
                 , createdOn = now
                 , workspaceId = workspaceId }
 updateSnapshot $ AggregateSnapshot
          { lastOffsetConsumed = 0
          , commandsProcessed = Set.fromList [commandId]
          , state = AggregateState { aggregateId = workspaceId } }
 updateSnapshot $ AggregateSnapshot
          { lastOffsetConsumed = 0
          , commandsProcessed = Set.fromList [commandId]
          , state = AggregateState { aggregateId = workspaceId } }
...