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 list

How 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 -> State

Given 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.
  • 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 Event coming from the Decider and change the state accordingly.
      • Linq .Aggregate().
  • 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)
    • 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

  1. https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider

  2. 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/

  3. 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.

  4. https://www.karlvanheijster.com/blog/22/06/testen-via-de-voordeur/

  5. 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.