Domain-driven design
Domain-driven design (DDD) is a concept to focus development on the business domain.
It has opinions on the structure of the code, the team, the processes and even the language
to use.
To teach myself (and others) about DDD, I made a demo web application about the board game
draughts. You can find it on
GitHub.
DDD's take on software architecture
DDD wants us to split our application in two ways, based on two questions: What are we talking about and how are we going to do it.
For the first part we split our application in bounded contexts and each of those we split
in aggregates.
Bounded contexts are parts of the application that operate separately and can only talk to
other contexts with event based communication. In the most extreme case, each bounded
context becomes a microservice.
Aggregates are parts of the domain model that update as a unit.
Once we've divided our application by subject of responsibility, we know what we are talking
about in general (the context) and which parts specifically (the aggregates).
Bounded contexts: | Game | User | Auth |
---|---|---|---|
Aggregates: |
Game, GameState, Voting |
User, Settings |
AuthUser, Role, AdminLog |
For the second part we split our application based on type using the hexagonal architecture
pattern.
This splits the tasks at hand in several layers, where parts in each layer are only allowed
to talk to parts in the same layer or deeper.
For a web application this typically means that we need to parse HTTP requests, interact
with a database and perform some sort of business logic. You do not want a database query to
worry about HTTP requests, or your business logic to rely on database queries.
Hence, domain objects (which are responsible for your business logic) are in deeper layers
than your repositories (who execute your queries), and controllers (which handle the
requests) are on the outside. It's customary to use a service as a middleman if multiple
repository or domain model calls are connected, but they can be skipped.
Example: win a game
Let's look at what happends when a player makes the final move and wins the game. To process this move, we need to access three aggregates across two bounded contexts. In the game context we first get some information from the game aggregate and then edit the gamestate aggregate to make the move. We raise an event to notifiy the user context. In there we pick up the event and update the statistics.