The cloud is a game-changer. Instead of building big applications that are hosted on giant machines, the industry is moving away from that and is moving towards serverless architecture. Everybody is talking about microservices. More and more developers are talking about hexagonal architecture (also known as Ports and Adapters), Domain-Driven Design and Command Query Responsibility Segregation (CQRS)
Part 1.) Creating the DDDomain model
Any application automates processes in a given domain. An application isn’t a CQRS or a WinForms application. It’s a billing application or a cab dispatching application.
Processes and decisions are automated in the domain model. It defines the interface users can interact with, and it describes the interface of the systems it interacts with. Like third party web services, e.g.
It all starts with the domain.
A domain is about euro’s, or kilometers, not about integers and strings
A domain model emerges from being explicit about the domain. In any domain, there are quantifiable things. For example, a cab driver might talk about distance and price. But are we talking kilometers or miles? And are we talking euro’s, bitcoins or dollars?
That’s where the domain model starts. Start by being explicit about what you’re talking about:
And implement that:


Moving away from integers or decimal, towards Kilometers or Euro’s, makes it harder to make mistakes. Consider this example:




Make the incorrect inexpressible!
Protect the business invariants!
Apart from value types like, for example, Kilometers or Euro’s, a domain contains more complex entities, too.
For example, a customer gets a ride. And typically, a ride changes over time: The customer enters the cab and states his destination. A couple of minutes later, the destination is known and the cab driver is driving. Even later, the customer gets off the cab and pays for the ride:


Note that the properties of any domain object do not have public setters. They do have public getters. That makes the domain model itself responsible for its integrity. Nothing from the outside can corrupt its data.
Entities contain most of the business rules in the domain. They contain most of the “if” statements of the application. By putting the domain logic inside of entities, the domain model becomes a rich domain model, instead of an anemic one.
As a rule of thumb, assume, any type of thing you’re modeling is a value type first. Start adding functionality to it as you go. As soon as it isn’t immutable anymore, it became an entity.
- Compare value types by the values, not by reference.
- Compare entities by their key values. Ignore the other fields.
Keep it small
Note that in the example above the “Ride” class does not reference the ”Car” class directly. It holds a reference to its id, instead. A reference from one entity to another entity potentially creates a huge object. Only reference value types in an entity, as a rule of thumb.
The service tells the story
Having all business logic wrapped in neat objects that represent the domain, leads to the next challenge: How to use the domain? Something needs to create and invoke this business logic, right?
Like in real life, how to use an object depends on a policy or a process. Consider, for example, a cab. Will dispatch contact the cab nearest to the customer’s location? Or will they contact the cab that’s nearest to the destination?
Describe a policy in a service. It tells the story:


A service is a part of the domain model. It’s defined in the domain model project and its interface is accessible to the outside world. Any type of application can invoke the domain model through the interface of the service.
- As a rule of thumb, services have no “if” statements.
- Services are the glue between systems. They transport information from a system to something else.
Sources:
Part 2.) Ports and adapters
Having a domain model is one thing. But a .csproj with no references to anything isn’t very usable. It needs a user interface and it needs to be connected to — for example — a database… Alistair Cockburn explained his ideas about how to do something like that: Hexagonal, or Ports and Adapters. A way to connect a DDDomain model to a user interface and to infrastructure.
A domain model is usually a single .csproj. Everything that interacts with the domain model should be in other .csprojects.
A domain model does not depend on anything, everything depends on the domain model
The domain model is the core of an application. Everything needs to be able to use it. To make that possible, the domain model cannot depend on anything. Everything depends on the domain:


A domain model describes the processes and policies of a company. A domain model should be a software developer’s documentation of the company. A domain model has nothing to do with technique.
As a rule of thumb, a domain model project depends on NETStandard.library only. No NuGet packages, whatsoever:


The domain decides what dependencies to implement and how the domain should be used
The domain model needs a programming interface. That interface is called the primary port. It allows something to interact with the domain. The interfaces of the domain services are the primary ports.


The domain model describes the interfaces of things it interacts with, too. Like persistence, or third party services. Those are the secondary ports. An example of a secondary port is a repository. Ports are defined inside of the domain model. Ports are not implemented in the domain.


Implementing the dependencies of the domain
Services invoke systems through secondary ports. They pass instances of the domain objects to them:
An adapter is something that implements a port. The implementation interacts with systems that aren’t a part of the application. Like a database. Or another web service. These systems cannot ensure their data to be compliant with the business rules that apply in the domain. Their data is often structured different, too. As a result, an adapter does the following:
- It maps a domain model to the data-contract of the other system
- It invokes another system
- It maps the response into a domain model.


Secondary ports are not implemented in the domain model. They’re implemented in separate projects that reference the domain model project. Group secondary ports by what they connect to and create .csprojects for those. For example:
- Dispatching.Persistence (connects to the database)
- Dispatching.Accounting (connects to, for example, third party accounting software)
Invoking the domain
Something needs to invoke the domain services. Those a CQRS that would be the command handlers. Instead of implementing an interface in the domain, they depend on an interface of the domain:


The command handler gets the concrete implementation of ICabRideService, defined in the domain, injected into it by the dependency injection framework.
Just like any other adapter, the first thing it does is mapping the request into something the domain understands. Then, it invokes the domain service. Next, it maps the response into an event.
Sources
Part 3.) CQRS
CQRS means Command and Query Responsibility Segregation. In other words, handling commands is done by one part of the application. Querying and searching entities is done by another.
The query model
The domain model handles commands. It has its own persistence mechanism to save the entities in.
Some domain models use event sourcing to persist the state of an entity. In that case, it might use a no-SQL database. But their query models often do use a SQL database to store data in. With CQRS, one persistence mechanism is connected to the domain model, and another contains the data that will be queried by the users. The domain model is unaware of the query model. As a result, changing the data-model the domain model depends on, does not require the read model to be changed.
Summarize the domain model
Typically, user interfaces show lists. Often, these lists contain a fraction of the data that is related to the listed objects. Retrieving a full-blown instance of a domain model doesn’t make much sense in that case, because most of the data will not be displayed. And querying a complex data structure comes with a performance penalty.
Take for example this Uber screen. It shows only a summary of the Cab domain model:


This example shows a simple, flat list of cabs. But it contains too little information to perform complex operations with this entity.
Think of a query model as a view in SQL server. It creates a simple, understandable, queriable table based on a complex table structure. The data is updated in the real tables, not in the view.
One big difference between views and CQRS is that the lists that are queried won’t necessarily be in the same database. They could be a different type of database. Meaning data needs to be transported from the write-model to the read model by our Dispatching application. That’s done through events and commands.
The command handlers tell a story, too
The write model is leading. After the write model has been updated, an event is queued. A process is listening and updates the query model. Every invocation of the domain starts with a command.
Command handlers invoke domain services and publish events for other handlers to pickup. Commands always imply something needs doing, and they always publish events implying something has been done:


Event handlers do never invoke domain services. All they do is create commands and publish those:


By using events and commands this way, the commands tell a story:
- DriveCustomerToStation
- DrivingCustomerToStationFailed
- ArrangeAlternativeRide
- AlternativeRideArranged
- InformTheCustomer
- CustomerInformed
Don’t do it yourself
Building and maintaining a queueing mechanism is a lot of work. Nowadays several open-source products provide queueing mechanisms and can easily be connected to Azure ServiceBus, RabbitMq or even in-proc. Have a look at:
Need an SLA?
Sources
Wrapping up
The Cab Dispatching example could result in the following project structure:
Dispatching
This is the domain. It does not have references to anything. It contains value types, entities, services, the interfaces of these services, and the secondary ports (IProvide interfaces).
Dispatching.Persistence
This project has a reference to the domain and it implements the repositories, has a reference to Entity Framework, and contains the data-model of the database.
Dispatching.Accounting
This project has a reference to the domain and it. It implements secondary ports that are defined in the domain. It contains service proxy classes to the SOAP service of your accounting software.
Dispatching.QueryModel
This project does not have a reference to the domain, and the domain does not have a reference to it. This project has a reference to Entity Framework. It defines a simplified, easily queriable, data-format. It contains repositories that update the query-model and query it.
Dispatching.Broker
This project has a reference to the domain and it has a reference to ReBus, MassTransit or NServicebus. It defines the commands and the events. It has command handlers that process the commands and it has event handlers that create more commands. It implements command handlers that invoke the repositories defined in the query model, too.
Dispatching.Api
This project has a reference to both the query model and the broker. It enqueues commands and it queries for data.
Example code
A full reference project is on GitHub, and an explanation about that project can be found here.
Summary
Creating a domain model starts by creating value types. They are new data types, specifically made for a domain. They’re used in entities. The services tell the story. They define the interactions between systems and entities. The domain model does not depend on anything, everything depends on the domain.
The domain model defines an interface that allows applications to invoke it. It defines an interface of the things it interacts with, too. Those are called the primary and secondary ports. They are interfaces.
The secondary ports are implemented in other projects. These implementations are the adapters. The primary ports are invoked by command handlers.
The domain model has its own persistence mechanism. It might be pretty complex. The summaries of the domain models are saved in another persistence mechanism that’s optimized for querying. The domain model has is unaware of the query model and the query model is unaware of the domain.
Use an API to enqueue commands and to invoke the query model.