Domain
The domain is the model of the business: its objects, its rules and what happens to them. It lives in domain/ and imports nothing but the domain and @alveolus/core.
Why
When the rule "an order cannot be placed empty" sits in a controller, a SQL query and a cron job, each copy drifts and nobody knows which one is right. When it imports an ORM or a framework, it cannot be read or tested without them.
The fix
The rules live in one place, in plain TypeScript classes named after the business. Everything else calls them.
How the blocks fit together
The building blocks
| Building block | What it is | Use it when |
|---|---|---|
| Aggregates | A group of objects changed together through one root, which keeps their rules. | Some rules must hold after every change. |
| Entities | An object defined by its identity, inside an aggregate. | A part of an aggregate changes over time and must be told apart. |
| Value objects | An immutable value compared by its attributes, and the typed identifiers. | A number or a string has rules or a unit: an amount, an email, an id. |
| Domain events | Something that happened in the domain, in the past tense. | Something else must react to a change. |
| Domain errors | An expected business failure, returned as a value. | A business rule refuses a request. |
| Domain services | A stateless operation that belongs to no single object. | A rule needs several aggregates and belongs to none. |
| Ports | What the domain needs from the outside world, in its own words. | The domain needs the time, an id, a payment, another context. |
| Repositories | How aggregates are loaded and saved, and how views are read. | An aggregate must be stored, or a query must read data. |
| Views | What a query returns. | A screen or an API needs data shaped for reading. |
See also
- Application, the use cases that call the domain
- Strategic, how contexts meet
- Rules:
layers/no-impure-domain,tactical/no-misplaced-class