# Deciders

This guide explains the **Decider pattern**: the write side of an event-sourced component as a small set of pure functions. It covers how the functions work together, why the decision and the state transition belong apart, and what the pattern changes for testing and structure.

Every event-sourced component does two things. It **decides** which events a command leads to, and it **evolves** its state from the events that have happened. Traditional aggregates bundle both into one class, with a method per command and a method per event, and shared mutable state in between. The Decider pattern, introduced by **Jérémie Chassaing**, separates them into plain functions with explicit signatures – nothing more is needed.

## The Three Parts

A decider consists of two functions and a value:

```
decide:       (Command, State) → Events
evolve:       (State, Event) → State
initialState: State
```

- **`decide`** takes a command and the current state, and returns the events that describe what happens – or a rejection. This is where all business rules live. It changes nothing; it only decides.
- **`evolve`** takes the current state and a single event, and returns the next state. It contains no business rules and no validation. The decision has already been made; `evolve` only records its consequences.
- **`initialState`** is the state before anything has happened.

## How They Work Together

For a book in a library, the state might say whether the book has been acquired and whether it is currently borrowed. When a command such as **borrow book** arrives, the same few steps always follow:

1. **Rebuild the state** by folding the past events of the book over the initial state with `evolve`.
2. **Decide** by calling `decide` with the command and that state. If the book is already borrowed, the result is a rejection; otherwise, it is a `book-borrowed` event.
3. **Store** the new events.

The same two functions do everything: `evolve` rebuilds the state from the history and keeps it up to date after new events, and `decide` applies the rules. There is no separate mechanism for replaying and another for handling commands – it is one loop: decide, evolve, repeat.

## A Functional Core

The Decider pattern is the event-sourced form of a broader principle, often called **Functional Core, Imperative Shell**: keep the logic that makes decisions free of side effects, and push everything that talks to the outside world – databases, clocks, networks – to a thin shell around it.

In a decider, the functional core is `decide` and `evolve`. The shell does the rest: it reads the events of a subject, calls the functions, and writes the new events, together with the **preconditions** that make sure nothing was written in between. Whatever a decision depends on must reach the core as a value. If a loan period depends on the current date, the date is part of the command or the state – `decide` never asks a clock itself.

This split has a simple consequence: the core can be **understood, tested, and changed without any infrastructure**, and the shell can be replaced without touching a single business rule.

## Why the Separation Pays Off

Keeping `decide` and `evolve` apart is not only tidier. It has practical effects that grow over time:

- **Testing needs no mocks.** Since both functions are pure, a test is a sentence: *given* these past events, *when* this command arrives, *then* expect these events – or this rejection. There is no database to fake and no clock to freeze – see **[Testing Event-Sourced Systems](/concepts/testing-event-sourced-systems)**.
- **The contract is visible.** The signature `(Command, State) → Events` says everything about what `decide` does. There is no hidden state, no lifecycle, and no order of initialization to know about.
- **Nothing grows unnoticed.** Aggregates tend to attract logic until they become hard to split. A decider has no class to grow into – adding behavior means adding a case to a function whose inputs and outputs stay explicit.
- **Deciders compose.** Two deciders with the same shape can be combined into one, with `decide` dispatching each command and `evolve` routing each event to the right part.

## What You Keep

The pattern is sometimes mistaken for giving up the structure that aggregates provide. It does not:

- **The boundary stays.** Only the `decide` function of a component produces its events. The encapsulation is expressed through a function signature instead of a class, but it is just as strict.
- **Any language works.** The pattern was introduced in F#, but `decide` and `evolve` are plain functions in TypeScript, Go, Java, or C# alike. It is about separating concerns, not about a programming paradigm.
- **Nothing is lost in expressiveness.** Complex rules, validations across several fields, and commands that produce several events are all a matter of what `decide` returns.

## Deciders and Consistency

A decider decides on the state of one **subject** – one book, one reader, one loan. The shell makes sure that the decision is still valid when its events are written: if another command changed the subject in between, the write fails instead of being based on a stale state. Where a rule spans several subjects, the same idea extends to consistency boundaries that are defined per decision rather than per aggregate – see **[Dynamic Consistency Boundaries (DCBs)](/concepts/dynamic-consistency-boundaries)**.

> **Try it with ArchitectureKit**
>
> In ArchitectureKit, a decider is a value that combines the state of a subject with a `Decide` function, and executing a command reads the events, evolves the state, decides, and writes with the command's preconditions – see **[Making Decisions](/docs/architecturekit/making-decisions)**.

## Designing Deciders

Keep `decide` about rules and `evolve` about facts. If `evolve` starts to validate, a rule has ended up in the wrong place; if `decide` starts to change state, a fact has. Give the state only what decisions need – it is not a read model, and nothing outside the decider has to see it. And pass everything the decision depends on as a value, so that the core stays pure.

Seen this way, the Decider pattern is not a new architecture. It gives names and types to what every event-sourced system already does – and in doing so, makes it easy to see, test, and change.
