The quote now has rules, events and a domain service. What it lacks is everything that isn't domain logic: it is stored nowhere, nobody can create it over HTTP, and someone has to hand it the office it belongs to. In this lesson all of that comes in, in such a way that the model notices none of it. The architecture for this is called Ports and Adapters, also known as hexagonal architecture after Alistair Cockburn.

At the end, the quotes are stored through a port IQuoteRepository, with two adapters behind it: one in memory for tests and one with EF Core for SQL Server. An endpoint sends quotes, the office comes from the token, and a test resolves every registration once so that none of them silently returns null.

Inside and outside

Ports and adapters divides a program into two areas: inside is what the application is, outside is what it connects to.

               +--------------------------------------------------+
  HTTP ------> | Adapter: QuoteEndpoints                          |
  Token -----> | Adapter: ClaimsCurrentUser                       |
               |   +------------------------------------------+   |
               |   | Application: QuoteCommands               |   |
               |   | Ports: IQuoteRepository, ICurrentUser    |   |
               |   |   +----------------------------------+   |   |
               |   |   | Core: Quote, Money, the rules    |   |   |
               |   |   +----------------------------------+   |   |
               |   +------------------------------------------+   |
  SQL <------- | Adapter: SqlQuoteRepository (EF Core)            |
  Sales <----- | Adapter: InProcessIntegrationEventPublisher      |
               +--------------------------------------------------+
  • The core is the domain model from the last four lessons: aggregates, value objects, events, domain services. Pure code, testable without anything else.
  • The application contains the use cases: “send a quote,” “grant a discount.” A use case loads, lets the model decide, saves and hands out the events. It contains no rule. What it needs from the outside, it describes as an interface, as a port.
  • The adapters sit on the outside and connect the ports to the world: SQL Server, HTTP, the sign-in token, later Orleans and Service Bus.

There are two kinds of ports. Driving ports are the entrances: what an adapter calls to make something happen. Here those are the methods of QuoteCommands, which the HTTP adapter calls. Driven ports are the exits: what the application needs and an adapter provides. IQuoteRepository is one, and so is ICurrentUser.

The most important rule isn't in any drawing, but in the project references: dependencies point inward. The adapter for SQL Server knows the port, the port knows the model, the model knows nothing of that. That is why IQuoteRepository lives in Kladde.Quotes.Application and not in the project with EF Core. Whoever defines a port owns it, and that is always the side that needs it. You know the idea from lesson 2.10.4 as dependency inversion; here it gets its name in the architecture.

Microsoft's guide (under Further reading) puts the interface of a repository into the domain model itself. The Kladde puts it one layer further out, in the application, because only use cases load and save; the model never calls a repository. Both point inward, and both are common. What matters is only that it is consistent across the whole Kladde.