Domain Driven Design, CQRS, Event Sourcing, Hexagonal Architecture & more
The road to the right software architecture can be very long. Especially when you're not a professional software architect who helps his customers with developing complex applications day by day.
What will be the Oregami way to the right software architecture? If you've been monitoring our project from the beginning, you know that we threw over the chosen technologies one ore two times already. In the beginning I only had the classical multi-tier architecture in mind and mainly thought about which Java framework to use for persisting our game objects and which database software to use for that. Until today my mind changed in many ways. When I started my first web project (kultpower.de) about 15 years ago with PHP3, I did not reflect about what was the best way to do it. With Oregami, it's different - I want more quality. Therefore I am researching technologies for the development of complex software applications in every free minute I can find. If you take a look at the people I am following on Twitter, you might recognize some well known developers from whom I learned about things like Spring Boot, HATEOAS, Responsive Web Design and Mobile First, JSON Web Tokens, Roca-Style and more.
My latest "achievements" are - again - having a deep impact on the big picture of the Oregami application. This summer I finally got some time to get into the topic "Domain Driven Design" (DDD), which Wikipedia describes as an "approach to software development for complex needs by connecting the implementation to an evolving model." You might think "so what?". I read the book "Implementing Domain Driven Design", which enlightened me in more than one way. Many of its topics are an obvious perfect match for Oregami development. During the last months, even before I knew about DDD, I found some similar solutions for small aspects of the whole thing myself, which creates a rather good feeling.
But what is "Domain Driven Design" all about in the end? I will try to resume the major parts of DDD as I understand them:
- Domain experts work very closely together with developers to create a "ubiquitous language" and describe the domain model with that language.
- The complete business model is not modeled in one big, comprehensive model, but is instead divided into multiple, smaller sub domains ("bounded contexts") according to some special rules.
- You distinguish between "real" entities and value objects, the latter should be used as much as possible, because they are in many aspects more controllable than entities. This gives you some major technical advantages.
- Every Bounded Context (BC) consists of exactly one network of entities ("Aggregate") with one main entity which is called "Aggregate Root". If a transaction alters the state of the application, only one aggregate at a time is allowed to be changed. It's not allowed to change more than one aggregate in one transaction.
- A connection from one BC to another may only reference the aggregate root of that BC. The aggregate root is responsible for all rules of that model. It's not allowed to span rules over several BCs.
During my investigations I also discovered the concept of the so-called "Hexagonal Architecture". Alistair Cockburn writes about it with this essential sentence: "Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases." Wow, that's exactly how software should be: universally useable, easy to expand and easy to test. This architecture concept is sometimes called "Ports and Adapters" or "Onion Architecture", too, starting from the inner application code of the core domain, going through the application service layer to the outer infrastructure, there are no dependencies from the inner to the outer parts. Doing this makes your core code independent of the technical infrastructure, which again leads to better testability (infrastructure "ports" can be swapped easily, e.g. with in-memory variants) and great extensibility (One can add an additional "adapter" to support more clients which use your software, e.g. via ReST or queues).
Talking about DDD and hexagonal architectures, you often stumble upon concepts like CQRS and Event Sourcing. Let us begin with Event Sourcing, which describes that the state of every business object is defined by a sequence of events. In practice this means a total rejection of the classic (mostly relational) persistence of data, which directly updates (replaces) the database state with every change. Instead every change of state is triggered by an event. These events are stored in the application, and reading requests are answered by "replaying" the saved events in the same order as they occured. This leads to nothing less then greater scalability and reproducibility of all changes. The latter is exactly what we do need at Oregami with an authoring system where updates to the central domain data (games & co) shall first be reviewed by peers before they are seen as permanent changes. Also suitable for this subject area is the concept of CQRS (Command and Query Responsibility Segregation): all reading access to the application ("queries") is strictly separated from the writing access ("commands"), which has positive influences on scalability. In my mind's eye, I can virtually see Oregami's users sending their input through commands, whose corresponding changes are saved as events. After having been reviewed (we just have to replay the stored events) they can be stored permanently and from now on can be used for all public reading access. This leads us to the user interface of our application: Unlike in our present prototypes, which all use the principle of CRUD, we should better use a "command oriented" user interface, also known as Task based UI. This concept is perfectly suited for commands or tasks from the approaches described above. In our Oregami domain we might think of commands like "Create Game", "Add Release" or "Add Screenshot".
What will we do with these insights?
I can imagine the following steps:
- I will - again - start a new Git repository: Oregami-DDD or something like that.
- We identify our first Bounded Context, which we will implement after the rules of DDD
- We identify and create the necessary entities and value objects.
- We model appropriate use cases and implement them with a hexagonal architecture.
- Changes to data are always initiated by commands and are stored as events.
- The events must be displayable and reviewable, for the peer review process.
Later on we will create more and more Bounded Contexts with the same ideas in mind to expand our application. It will be challenging to see how the different BCs communicate with each other and how the user interface can integrate multiple BCs. We will also have to see if we really do need a separated read model.
For the interested reader here are some of my favorite sources for my inspirations:
- Vaughn Vernon: Effective Aggregate Design (PDFs: Part 1, Part 2, Part 3)
- Paul Rayner: Aggregates & Entities in Domain-Driven Design
- Alistair Cockburn: Hexagonal Architecture
- Slideset by Jeppe Cramon: Agile, Architecture, DDD and CQRS
- Martin Fowler: Event Sourcing
- Torben Fojuth: Domain-Driven Design im Hexagon (German)
- Kyle Cordes: Task Based User Interfaces
- Mehdi Khalili: ORM anti-patterns - Part 5: Generic update methods