jonnify·bcs /bcs/trading/engine/the-decider

B8.1 · dive 2 — the Decider

What a command means: events out, never a mutation.

The decision half of the engine answers the second question: what a command means when it meets a book. The functional event-sourcing Decider, as Chassaing fixed it, is three types and three elements — initialState, decide : Command -> State -> Event list, and evolve : State -> Event -> State. decide is the only place business rules live: it returns the facts that follow, never a mutation. evolve folds a fact into state, and it is deliberately trivial.

Exchange.Decider and Exchange.OrderBook are PROPOSED pure modules; Exchange.Book is the PROPOSED GenServer shell (docs/trading/). They are taught as design; none has a rung yet, and none claims a number.

The decide / evolve stepper. A tiny fixed book: one resting ask of 5 @ 100 (id ORD-a1). Feed a command; decide computes the events (pure), then evolve folds each into the new state. No process, no store — the fold law, visible.
command decide pure · events out evolve pure · fold new state
book · resting ask ORD-a1: 5 @ 100. Feed a command — decide returns the events, evolve folds them to the new state. Every line is computed by the two pure functions over the fixed book.
§1

The right seat for a matching engine

docs/trading/trading.patterns.md · Part one

The pattern is three types and three elements, quoted from the corpus:

initialState : State
decide       : Command -> State -> Event list
evolve       : State -> Event -> State

decide is the only place business rules live: given a command and the current state, it returns the facts that follow — possibly several, possibly none, never a mutation. evolve folds a fact into state, and it is deliberately trivial — "Given current State, when Event occurs, here is the new State" — a field set, a list append, a counter move; decision has already been taken. The discipline the two signatures enforce is the whole value: rules cannot hide in mutation, because there is no mutation; state cannot drift from the log, because state is the fold of the log.

Three properties a matching engine cannot negotiate away, each a direct corollary of the signatures. Testability without machinery — price-time priority, self-trade prevention, partial-fill arithmetic are properties over decide alone: feed a book state and a command, assert on the returned events; no process, no store, no mock. Replay as identity, not feature — fold evolve over the log and the book is its history; the Chapter 4.4 journal already gates exactly this shape (replay reproduces live state), so milestone A inherits a certified mechanic rather than promising one. Audit as a property — every fill is explainable as decide(cmd, state_at_seq), and because sequence is the branded mint order, "state at sequence" is a well-defined point, not a race.

§2

Event sourcing, not command sourcing

docs/trading/trading.patterns.md · the distinction

One distinction worth pinning because the literature blurs it: this is event sourcing, not command sourcing. The log stores decide's outputs — the facts — not its inputs. Storing commands and re-deciding on replay couples history to every past version of the rules; storing events makes replay a pure fold under evolve, which rarely changes and changes safely. The platform logs facts.

The replay corollary is the one place the design leans on the as-built floor: the Chapter 4.4 journal already gates "replay reproduces live state," so the book's replay rung re-gates a posture this course committed, rather than inventing one. The journal's record is committed today; the book's own replay rung is not yet run, and so the design claims no number for it.

The three corollaries follow from the one choice: testability is a property over decide; replay is the fold the B4.4 journal already gates; audit is decide re-applied at a sequence the id law makes exact.

§3

Alternatives weighed

docs/trading/trading.patterns.md · the honest comparison

The pattern earns its seat against the alternatives, named honestly. Select a row to read how each fares against the three properties and the seam it costs.

ApproachTestable rulesReplay freeAudit a property
the Decideryesyesyes
the mutating aggregatedrags machinerybuilt-for, not freevia the entity
transaction script / CRUDover rowsno foldforensic only
the saga as decision seatworse testsone level upacross aggregates
actor state, no eventsover a processsurrenderedsurrendered
readout · Select a row. The Decider scores all three by signature; each alternative trades one or more away, and the corpus names the cost rather than hiding it.
§4

Functional core, imperative shell

docs/trading/trading.patterns.md · the BEAM placement

The functional core, imperative shell, as the house already practices it: Exchange.Decider and Exchange.OrderBook are pure modules; Exchange.Book is the GenServer shell that owns the process identity, drains the ingress Ring, calls decide, appends the events to the log, folds evolve, and answers. The shell is thin enough to be boring, which is the point — everything worth testing is underneath it, and everything worth supervising is the shell.

All three Exchange.* modules are PROPOSED — the trading engine on top of a real substrate. The shell composes EchoCache.Ring (dive 1) and, at the next module, EchoCache.Journal — both shipped Elixir in the live umbrella, hardened by the rung-gated EchoMQ program (/echomq, docs/echo_mq/emq.roadmap.md), which names the trading platform as its downstream consumer. The pure cores are the engine's to build, and they earn their first numbers at their own rungs; the substrate beneath them already has its numbers. Where the Decider is the wrong tool, named: cross-aggregate invariants — "this account's margin across all books" — do not belong inside one book's decide; they are the saga's job, downstream of the log, with compensating events. Long or effectful work inside decide is a category error: decide is pure by signature, and IO belongs at the shell. The order book whose priority falls out of the id law is B8.1.3.

§5

References

sources · related

Sources

Related