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 }

Benefits and rationale.

  • Union type supports making illegal states unrepresentable.5
    • Parse don’t validate6
  • Events allow for decoupling and map well to a DDD + EventStorming approach.
  • Aligns well with the goal of an aggregate.

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.
  • I’m not quite sold yet on the implementation.
    • Does it have to be the Decider pattern or can you reach the goal as well by adhering to the shape in terms of what you assert, where you put the input and output boundaries of what you’re putting under test?
  • I’m sold on the implementation if it is paired with Orleans.NET and can very well contain the business logic of a grain/actor.
  • Based on commands and events the decider is the entry-point to change state. Basically can be modelled like a state machine, the decider is a form it that it seems. Especially when I look how I implemented it within a MassTransit consumer, I could even see this working with an actual MT statemachine.7

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 pattern8. 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. https://kb.evryg.com/en/advanced-software-engineering/type-design/make-illegal-states-unrepresentable

  6. https://kb.evryg.com/en/advanced-software-engineering/type-design/parse-dont-validate

  7. https://kb.evryg.com/en/advanced-software-engineering/type-design/state-machines-and-aggregates-in-ddd

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