type | tags |
---|---|
slide |
dddeurope |
Marco Perone
working at Tweag
We will be using Gitpod
https://github.com/marcosh/ddd-machines-dddeurope
Events relevant for domain experts
User intentions/actions/decisions
Data needed in order to make decisions
Decide what happens on commands
Reactive logic that takes place after an event
Aggregate data from events
data Mealy state input output = Mealy
{ initialState :: state
, action :: state -> input -> (state, output)
}
We are building a lending application
We collect information about the user and the loan in order to decide wheter to grant the user the requested loan
graph TD
CollectUserData --> CollectLoanDetails
CollectUserData --> QueryCreditBureau
CollectLoanDetails --> AllDataCollected
QueryCreditBureau --> AllDataCollected
![Event storming](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/event-storming.jpg =600x600)
From commands to events
Let's implement the aggregate for our domain
To compile the code and run the tests
stack test --file-watch
data RiskEvent
= UserDataRegistered UserData
| LoanDetailsProvided LoanDetails
| CreditBureauDataReceived CreditBureauData
data RiskCommand
= RegisterUserData UserData
| ProvideLoanDetails LoanDetails
| ProvideCreditBureauData CreditBureauData
What is the state space?
data RiskState = _
Next we need to implement
action :: RiskState
-> RiskCommand
-> ([RiskEvent], RiskState)
and
initialState :: RiskState
From events to read models
We want to project the received data, which could or could not be there
data ReceivedData = ReceivedData
{ userData :: Maybe UserData
, loanDetails :: Maybe LoanDetails
, creditBureauData :: Maybe CreditBureauData
}
The projection is a state machine with RiskEvent
as input and ReceivedData
as output
What is the state space?
We can define it by implementing
action :: ReceivedData -> RiskEvent -> ReceivedData
and
initialState :: ReceivedData
From events to commands
Policies are the only one allowing side effects
A policy is an effectful state machine with RiskEvent
as inputs and RiskCommand
as outputs.
Our policy needs to react to the UserDataRegistered
event and send the data to the credit bureau, using the interactWithCreditBureau
function
To implement it, we just need to implement
action :: RiskEvent -> IO [RiskCommand]
How do we connect the pieces, now?
feedback
:: Mealy command [event]
-> Mealy event [command]
-> Mealy command [event]
Allows us to combine Aggregate and Policy
instance Category Mealy where
(.) :: Mealy b c
-> Mealy a b
-> Mealy a c
Sequential composition
Allows us to compose Write
and Read
sides
Let's try to run it!
-- risk/Main.hs
main :: IO ()
main = print =<< runApplication riskManagerApplication
[ RegisterUserData myUserData
, ProvideLoanDetails myLoanDetails
]
To execute it
stack exec ddd-machines-dddeurope-risk
Suppose we want to add another projection
userDataUpdatesCounter :: Projection RiskEvent (Sum Int)
Let's adapt our application to incorporate it
We can use
(&&&) :: Projection a b
-> Projection a c
-> Projection a (b, c)
to run the two projections in parallel
Time for question and remarks
Thank you all!