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 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
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).
For the second part we split our application based on type using the hexagonal architecture
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.