Recently I heard about the decider pattern and want to understand it and see what it can bring.
A couple of days later I’ve started by reading about the decider pattern on the website of the originator of the pattern Jérémie Chassaing1 .
I am also aware of a talk on DDD Europe on Aggregates Composition: A new view on aggregates. by Jérémie.
First impressions of the pattern
It seems to very much resemble a reducer2, something that based on a an action and a given state reduces the action to a new state. It reminds me a little bit of my experiences with Elm. Which also uses union types, and supports them just as F# natively. Not weird because both are functional languages and F# is what Jérémie uses in his talks and write up.
From what I understand reducers and Elm fall under Reactive Programming. The declarative nature of reactive programming is something what I see reflected in the decider pattern.3
I can understand why this would wrap your business logic in such a way that you can write behavioral tests that allow you to refactor within. And create a testing harness that is less coupled to the implementation. It creates a very functional frontdoor4 and should provide you with the freedom to refactor without the tests adding additional friction.
F# decider pattern and parts of it as described by Jérémie
Just to get the shape and the general idea, not to completely re-hash his blog and writeup.
He describes 7 parts of a Decider:
- A Command type that represents all commands that can be submitted to the Decider
- An Event type that represents all events that can published by the Decider
- A State type that represents all possible state of the Decider.
- An Initial state that represents the state of the Decider before anything happens.
- A decide function that takes a Command and a State and returns a list of Event.
- An evolve function that takes a State and an Event and returns a new State
- An isTerminal function that takes a State and returns a boolean value.
Let’s look at the parts a little bit more closely.
What a decide function could look like:
// The type signature of the function that could implement such a decision
type Decide = Command -> State -> Event listHow he summarizes this function:
When asked to process this Command in a given State, here is what happens, expressed as Events.
He describes that the decide function explicitly does not change state, I understand it to separate the decision making from the state changing or new state computing.
So he describes an Evolve function:
type Evolve = State -> Events -> StateGiven a current State, when Event occurs, here is the new State.
// c is the Command type
// e is the Event type
// s is the State type
type Decider<'c,'e,'s> =
{ decide: 'c -> 's -> 'e list
evolve: 's -> 'e -> 's
initialState: 's
isTerminal: 's -> bool }Adapting the Decider pattern into C#
- Decide method is for outputting a domain event based on an incoming command or event.
- Type signature:
Command -> State -> List<Events>. It takes a command and state and returns a list of events.- Nice that means more than one thing can happen when something changes?
- It does.
- Nice that means more than one thing can happen when something changes?
- Type signature:
- Evolve method is for changing the state based on the current state and the incoming event.
- Type signature:
State -> Event -> State - The intent is to loop over each
Eventcoming from the Decider and change the state accordingly.- Linq
.Aggregate().
- Linq
- Type signature:
- First implementation of decider pattern in repository
- https://forgejo.matthijs.tech/experiments/dotnet-decider-pattern
- Commands vs Actions
- There’s a relevant section in the blog post by Jeremie.
- The point is that for an event a command counterpart could be considered.
- Hence a decider only takes commands (which express an intent to change something)
- The point is that for an event a command counterpart could be considered.
- There’s a relevant section in the blog post by Jeremie.
- Composing different deciders together
- I will have to implement a compose function.
Comparing for understanding
Let’s look a little bit at the ‘shape’ of both a reducer, the elm architecture now that we’ve seen the decider pattern5. I am wondering if it’s also just a very natural pattern coming from functional programming. Let’s implement a counter to keep the example simple enough.
Javascript - Redux reducer
// reducer.js
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
// Check to see if the reducer cares about this action
if (action.type === 'counter/incremented') {
// If so, make a copy of `state`
return {
...state,
// and update the copy with the new value
value: state.value + 1
}
}
// otherwise return the existing state unchanged
return state
}
// usage
const actions = [
{ type: 'counter/incremented' },
{ type: 'counter/incremented' },
{ type: 'counter/incremented' }
]
const finalResult = actions.reduce(counterReducer, initialState)
console.log(finalResult)
// {value: 3}Elm Architecture
I love the Haskell like type annotations 💛
-- Main.elm
type alias Model = Int
-- The initial state
init : Model
init =
0
-- UPDATE
-- A union type describing the events that can occur
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
-- VIEW this renders the html
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]Footnotes
-
https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider ↩
-
https://redux.js.org/ originally created by Dan Abramahov is a implementation of the reducer pattern. After he was inspired by The Elm Architecture. Which is based on this paper on functional reactive programming. I wrote about Elm in 2017: https://infi.nl/nieuws/why-i-became-an-elm-evangelist/ ↩
-
I am not entirely sure of this is a very strong relation, perhaps this is mostly because in general functional programming is more declarative then let’s say procedural programming where steps are expressed. ↩
-
https://www.karlvanheijster.com/blog/22/06/testen-via-de-voordeur/ ↩
-
Not sure I will learn anything from it, if not it might just be for posterity and a way to attach the idea of a decider to concepts I’ve worked with in the past. ↩