In lesson 4.4.1 you decided that sales stores its deals as a sequence of events. In this lesson you build the aggregate for it: the Deal with its commands, events and rules. It will consist of two pure functions that know neither a database nor a clock. Storing comes only in the next lesson; here it is just about the model and its tests.

You know the building blocks from module 4.3: an aggregate with invariants, expected errors as Result<T>, domain events in the past tense. What is new is that the state is no longer stored but built from events. That changes what the aggregate looks like, and it makes the tests simpler.

The path of a deal

You know the course of events from the event storming in lesson 4.2.3. A deal begins as a tip about an opportunity, gets checked, gets one quote or several, and ends won or lost. In the glossary the sales opportunity is called Deal, and its stages are called this:

Stage in the code how to get there
New Lead open the deal
Qualified Qualified estimate the value and the expected closing
Quote Proposal attach a quote
Won Won win with an attached quote
Lost Lost lose with a reason

The rules that the office owner named in lesson 4.2.3:

  1. A deal has a customer and a title of at most 200 characters. The title names a project, never a person.
  2. A deal is qualified once, from the stage New, with a value above zero and an expected closing that is not in the past.
  3. A quote is attached only to a qualified deal. Attaching the same quote twice changes nothing.
  4. A deal is won only with a quote that is attached to it, and with an amount above zero.
  5. An open deal is lost in any stage, with a known reason.
  6. A won or lost deal never changes again.