Domain-Driven Design (DDD) defines many concepts and patterns that help design software effectively, in line with the business requirements. Axon separates infrastructural concerns from the domain logic, which, combined with DDD best practices, keeps complexity to a minimum.
A business operates within a domain, and that's what we're trying to design and model with DDD. We can derive more concrete definitions of a 'Domain' in the context of DDD:
- A concept that lives in a 'problem space'.
- The subject area to which the user applies a program.
- A sphere of knowledge, influence, or activity.
Domains can be very complex, and different areas within a domain might have different expertise. For example, in the Domain of Banking, there is a clear difference between consumer banking, corporate banking, and wealth management.
Typically, we can split the complex domain into subdomains, where each subdomain corresponds to a different part of the business. Identifying subdomains requires an understanding of the business, mostly its organizational structure and areas of expertise.
There are numerous techniques to discover a domain. Event Storming is an exciting one. It is a workshop format for quickly exploring complex business domains.
The result of this strategic process of exploring and decomposing complex domain is a set of smaller subdomains that should be categorized by importance:
- Core (sub)domain - most important part of your domain - the money maker - key differentiator for the business
- Supporting - related to what the business does but not a differentiator. These can be implemented in-house or outsourced.
- Generic - not specific to the business and are ideally implemented using off-the-shelf software
A model is: "A system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain;."
In other words, a model captures what's important and helpful in solving a specific problem within our domain. We can derive more concrete definitions of a 'Domain Model' in the context of DDD:
- A concept that lives in a 'solution space'.
- A software programming model which is applied to a specific domain (problem area)
Domain Model should define the vocabulary and should act as a communication tool for everyone involved (business and IT) deriving a Ubiquitous Language. This language needs to be rigorous since the software doesn't cope well with ambiguity.
Axon & Domain Model
Axon offers a mature programming model (AxonFramework) for crafting tactical building blocks of a Domain Model:
- Aggregate root
- Value object
- Domain event
An Aggregate is an entity or group of entities always kept in a consistent state (within a single ACID transaction). The Aggregate Root is the entity within the aggregate that is responsible for maintaining this consistent state. This makes the aggregate a prime building block for implementing a domain model in Axon-based applications.
Complex business logic often requires more than what an Aggregate with only an Aggregate Root can provide. In that case, the complexity must be spread over several Entities (aggregate members) within the aggregate. Axon provides support for event sourcing complex aggregate structures like these out of the box, with the ability to handle messages on the Entity level.
A Value Object is an immutable type that is distinguishable only by the state of its properties. Value Objects represent concepts in our Ubiquitous Language, and so they should always tell a story about the problem we are solving. For example `CustomerName` is a value object holding two `String` properties: `firstName` and `lastName`. Axon is encouraging us to use Value Objects in a way we think fits.
In Axon, Aggregates accept business commands, which usually produce an event related to the business domain – the Domain Event.
In CQRS based applications, Aggregate is very explicitly presented in the Command Model, as that is where change is initiated. However, Query Models / Projections are also built up of Aggregates. Generally, however, aggregates in Query Models are much more straightforward, as state invariants are generally less strict in those models.
Axon explicitly separates the business logic from the configuration of the infrastructure in which this logic needs to operate. AxonServer is an infrastructural component of Axon and takes care of storing and routing events and other types of messages (commands and queries) optimally and reliably. This separation makes Axon very flexible and configurable (with alternative components available for RDBMS, NoSQL, AMQP, Kafka, etc). More importantly, it allows you to focus on getting the domain logic implementation right, without distraction from infrastructural concerns.
A context is "The setting in which a word or statement appears that determines its meaning".
In other words, the same domain concept may have a different meaning to different people.
Consider, for example, the concept of a Flight. For a passenger, a Flight is a period between the departure of an aircraft until the arrival at your destination. However, the Ground Crew cares about the arrival of a Flight at the Gate, the number of meals, pillows, etc., to get on board, and they're done when the flight leaves the gate. For them, departure time is a deadline, not the starting point.
A goal is to develop a Ubiquitous Language as our domain (subdomain) model within an explicitly Bounded Context.
Therefore, there are several rules for Models and Contexts:
- Explicitly define the context within which a model applies.
- Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas.
- Keep the model strictly consistent within these bounds, but don't be distracted or confused by issues outside.
- Ideally, keep one subdomain model per one Bounded Context.
Axon & Bounded Context
Axon deals with Bounded Contexts in a few different ways. From the Framework perspective, separating business logic from configuration allows logic to focus on the relevant aspect of the context itself, using the configuration of serializers, upcasters, etc., to explicitly define how messages and interactions are shared beyond the boundaries of the context of the application itself.
AxonServer explicitly supports bounded contexts by allowing different (groups of) applications to connect to different contexts within AxonServer. Unless specifically indicated otherwise, contexts are strictly separated, and information is not shared between them.
You can read more about bounded context; click here.
A bounded context never lives entirely on its own. Information from different contexts will eventually be synchronized. It is useful to model this interaction explicitly. Domain-Driven Design names a few relationships between contexts, which drive the way they interact:
- partnership (two contexts/teams combine efforts to build interaction)
- customer-supplier (two contexts in upstream/downstream relationship - upstream can succeed independently of downstream contexts)
- conformist (two contexts in upstream/downstream relationship - upstream have no motivation to provide to downstream, and downstream context does not put the effort in translation)
- shared kernel (explicitly, sharing a part of the model)
- separate ways (cut them loose)
- anti-corruption layer (the downstream context builds a layer to prevent upstream design to 'leak' into their own models by transforming interactions)
Axon & Context Mapping
In Axon-based application, the context defines the boundary in which Events carry value. Some events may be valuable only in the context in which they are published, while others may be valuable even outside. The broader the scope in which an event (or any message, in that respect) is published, the more components end up coupling to the sender. Axon is a flexible platform that lets us choose any of the context mappings we find fit for our organization.
Effective design with DDD and Axon
Axon originated in an attempt to find a solution to the ever-increasing accidental complexity. Applying concepts from Domain-Driven Design will help to a substantial degree, enabling us to design our domain model effectively (in line with the requirements).
While Axon is opinionated on how the interaction with a domain model should occur, it tries to avoid any restrictions on the modeling freedom that one has. Even when your opinion differs from that of Axon, there are enough hooks, configuration options, and triggers to change certain aspects of Axon's behavior.
September 28th, Amsterdam
Join us for the AxonIQ Conference 2023, where the developer community attends to get inspired by curated talks and networking.
September 27th, Amsterdam
The event to collaborate, discuss, and share knowledge about techniques, tools, and practices for building complex, distributed event-driven applications.