Skip to content

Concepts: a reading path

Summary. These pages explain the ideas this kit is built on: Domain-Driven Design (strategic and tactical), the layered architecture and its dependency rule, messaging and consistency between bounded contexts, the cross-cutting concerns, clean code and testing. They teach and give the reasons; the rules themselves are stated once, in the architecture reference, and each page links to them by rule id. Every page can be read on its own in a few minutes. Read in order, they form a path from "what is a domain" to "how do I test this", using one example throughout: an appointment scheduling application.

The reading path in six sections: strategic design (pages 1 to 5), tactical design (6 to 11), architecture (12 to 15), messaging and consistency (16 to 22), cross-cutting concerns (23 to 34), code and tests (35 and 36), each with the question it answers

Clients book appointments with practitioners. Five bounded contexts are built, and identity is bought:

Context Classification Owns
appointments core booking, rescheduling and cancellation rules; no practitioner is booked twice
availability supporting the open slots, computed from calendars and booked appointments
calendars supporting the working hours and time off of each practitioner
notifications supporting confirmations and reminders, through email and SMS providers
analytics supporting occupancy and no-show statistics
identity generic an OpenID Connect provider; no context in the code

How they depend on each other, upstream to downstream, as the worked example of the context map finds it:

The running example: calendars publishes to appointments and availability; appointments, the core context, publishes to availability and notifications, and analytics conforms to it; the email and SMS provider is upstream of notifications, which keeps an anticorruption layer; the identity provider issues the tokens of every context

The example application of the repository, examples/scheduling, is a smaller cut of the same domain that runs; several pages quote its code.

1. Strategic design: where the boundaries are

Section titled “1. Strategic design: where the boundaries are”

Read these first. They decide everything that follows, and they are decisions of people, not of tools.

  1. Domain and subdomains: core, supporting, generic, and how to classify.
  2. Bounded context: what a boundary is and the tests that find one.
  3. Ubiquitous language: the glossary, and why it is written before the code.
  4. Context map: the relationship patterns between contexts, when each fits and what it costs.
  5. Context map: a worked example: from a plain statement of needs to the map of the scheduling application.

2. Tactical design: what is inside a context

Section titled “2. Tactical design: what is inside a context”
  1. Entities and value objects: identity versus value.
  2. Aggregates: the consistency boundary, one transaction, reference by id, sizing.
  3. Domain building blocks: domain event, domain service, repository, factory.
  4. Invariants, permissions, validation: three different things, three places.
  5. Anemic model: how to recognise one, and when a thin model is fine.
  6. State machines: the lifecycle of an aggregate declared once, asked by the aggregate, drawn in its CONTEXT.md.
  1. Layers and the dependency rule: hexagonal, onion and clean architecture, what they share and where they differ.
  2. Ports and composition: ports as abstract classes, the composition root, declarations by decorators or explicit wiring.
  3. CQRS: command and query buses as used here; event sourcing is a separate choice, per aggregate. The presentation layer comes with it: one controller class per route, route groups, guards at the edge and permissions on the use case; and representations, field access and forms: one schema answered in JSON, XML or CSV, fields reserved to some callers, forms described by the server.
  4. Modular monolith: one deployable, real modules.

4. Messaging and consistency: how contexts collaborate

Section titled “4. Messaging and consistency: how contexts collaborate”
  1. Domain and integration events: the two kinds and what separates them.
  2. Transactional outbox: why publishing straight to a broker loses or invents events, and the relay strategies.
  3. Delivery and idempotency: at-least-once, idempotent consumers, the processed-event store, ordering.
  4. Eventual consistency: explained to a product person.
  5. Sagas and process managers: what the kit provides and what it does not.
  6. Event versioning: changing a contract that others consume.
  7. Event sourcing, per aggregate: when an aggregate keeps its events instead of its state, what it costs, and how to move one to it and back.

5. Cross-cutting concerns: what surrounds the use cases

Section titled “5. Cross-cutting concerns: what surrounds the use cases”
  1. Authentication and authorization: roles, attributes, relationships, with a decision table.
  2. Authorization in practice: @Requires, the middleware, the Authorizer port and policies, followed through one command.
  3. Actor and execution scope: who acts, explicitly, and what every unit of work carries.
  4. Multitenancy: one deployment, many customers, and no read or write that crosses from one to another.
  5. Audit trail: who did what, recorded from outside the use cases.
  6. Observability: tracing, logs, metrics, error reporting, and the correlation id that ties them.
  7. Resilience: retry, timeout, circuit breaker, bulkhead, cache, and how each one fails.
  8. HTTP caching: what to cache at which layer, who may keep an answer, and never a decision of authorization across actors.
  9. Scheduled jobs: work triggered by time, and why it must be idempotent.
  10. Background jobs: work a use case hands to a worker, enqueued in its transaction, run at least once.
  11. File storage: files in a storage and never in the database, the order that keeps both consistent, signed links.
  12. File import and export: a design, not built yet: a large file read or written by a background job, row by row, between the queue and the file storage.
  1. Clean code: naming, small functions, stepdown, comments, testability by construction.
  2. Testing strategy: fakes rather than mocks, builders, contract tests, the four levels.
  • A product person or a newcomer to DDD: 1, 2, 3, 5, then 19.
  • Before writing a first use case: 3, 7, 9, 12, 14, 36.
  • Before making two contexts talk: 4, 16, 17, 18, 21.
  • Before keeping an aggregate as its events: 7, 16, 17, 21, 22.
  • Before serving several customers from one deployment: 23, 25, 26.
  • Before putting the system in production: 23, 24, 28, 29, 30, 31, 32, 33, then the tools pages.

These pages describe one opinionated way of applying the patterns, the one this kit enforces. Other valid applications exist (event sourcing, a separate read database, aggregates persisted as documents), and several pages say where the kit stops. The patterns themselves have a cost: for a small tool with little business logic, most of sections 2 to 4 is more than the problem needs, and each page has a "Limits and when not to use it" section that says so in its own terms.