Creating universal specifications in Haskell

As an exercise to wrap my brain around Haskell types and types, I am trying to implement a simple DDD / CQRS style solution. I model it directly after the implementation of Lev Gorodinski F # Simple CQRS .

I implemented a very simple aggregate Vehicle, which is a more or less direct port of the lion InventoryItemaggregate

module Vehicle where

data State = State { isActive :: Bool } deriving (Show)
zero = State { isActive = True }

type Mileage = Int 

data Command = 
    Create String Mileage
    | UpdateMileage Mileage
    | Deactivate
    deriving (Show)    

data Event =
    Created String
    | MileageUpdated Mileage 
    | Deactivated
    deriving (Show)

-- Define transitions from a command to one or more events
execute :: State -> Command -> [Event] 
execute state (Create name mileage)   = [Created name, MileageUpdated mileage]
execute state (UpdateMileage mileage) = [MileageUpdated mileage]
execute state Deactivate              = [Deactivated]    

-- Apply an event against the current state to get the new state
apply :: State -> Event -> State
apply state (Created _)        = state
apply state (MileageUpdated _) = state
apply state Deactivated        = state { isActive = False }

, , , , . Aggregate<'TState, 'TCommand, 'TEvent>, , Aggregate. Haskell , Typeclasses. , , , , .

, Aggregate Typeclass execute apply, -, , , . , Aggregate.

class Aggregate a where =
    execute :: state -> command -> [event]
    apply   :: state -> event   -> state

state, command event - , , Aggregate. , .

Typeclasses? , , Aggregate , ?

, Aggregate ?

@jberryman, MPTC , , :

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}

module Aggregate where

class Aggregate state command event
    | state -> command event, command -> state event, event -> state command where
        execute :: state -> command -> [event]
        apply   :: state -> event   -> state
        zero    :: state

makeHandler (load, commit) = 
    (\ (id, expectedVersion) command -> 
        do events <- load id
           let state = foldl apply zero events
           let newEvents = execute state command
           commit (id, expectedVersion) newEvents)

Vehicle

instance Aggregate State Command Event where
    execute = Vehicle.execute
    apply   = Vehicle.apply
    zero    = Vehicle.zero

script :

import Aggregate
import Vehicle

-- Mock out IO to a domain repository (EventStore)
load :: Int -> IO [Event]
load id = do return [Created "Honda", MileageUpdated 15000]
commit (id, expectedVersion) events = putStrLn (show events)

-- Create the handler provide a command to it
handler = makeHandler (load, commit)
handler (1,1) Deactivate
+4
2

state, command event , , MultiParameterTypeClasses FunctionalDependencies

class Aggregate state command event | state -> command event, command -> state event, event -> state command  where
    execute :: state -> command -> [event]
    apply   :: state -> event   -> state

| : " , ..." .. , . .

, :

  • , ?
  • , , ?
  • - ?

, , , . , .

, , , - :

data Aggregate state command where 
   Aggregate :: (state -> command -> [event]) -> (state -> event   -> state) -> Aggregate state command

GADT, event .

+4

F #, , , , Aggregate - . , , :

  • ,

, @jberryman, :

class Aggregate state command event | event -> state command where
  eventsToState :: [event] -> state
  execute :: state -> command -> [event]

FunctionalDependencies.

​​ , :

handle :: Aggregate state command event => command -> [event] -> [event]
handle command = flip execute command . eventsToState

Aggregate , OP:

instance Aggregate State Command Event where
  eventsToState = foldl folder zero
    where
      folder state (Created _)        = state
      folder state (MileageUpdated _) = state
      folder state Deactivated        = state { isActive = False }
  execute _ (Create name mileage)   = [Created name, MileageUpdated mileage]
  execute _ (UpdateMileage mileage) = [MileageUpdated mileage]
  execute _ Deactivate              = [Deactivated]

GHCI:

*Vehicle> handle (Create "BMW 520i" 250000) [] :: [Event]
[Created "BMW 520i",MileageUpdated 250000]
*Vehicle> handle (UpdateMileage 256000) it
[MileageUpdated 256000]
*Vehicle> handle Deactivate it
[Deactivated]

F #, , , , .

+4

Source: https://habr.com/ru/post/1679355/


All Articles