DDD consists of a lot of concepts, and you may feel overwhelmed by them at the very beginning. That's why I want to introduce you to them so that you can easily understand what is this all about.
It is a subject of matter that we are building software on. A sphere of knowledge, influence, or activity to which the user applies a software. For example, In a cafe domain, our focus will be on the concept of a tab, which tracks the visit of an individual or group to the cafe. When people arrive to the cafe and take a table, a tab is opened. They may then order drinks and food. Once the chef has prepared the food, it can then be served. Finally, the visitors close the tab by paying what is owed, possibly with a tip for the serving staff. Upon closing a tab, it must be paid for in full.
A common, rigorous language to help communication between software developers and domain experts. A language structured around the domain model and used by all team members to connect all the activities of the team with the software.
- It describes something that must be true with your design all the time.
- Invariants help us to discover the Bounded Context.
- An invariant is a statement about the domain that holds true, no matter what. By enforcing invariants, we ensure consistency in the domain model. This allows us to write code in all the layers that surround our model, without having to worry about this consistency.
- As an example, let's introduce an invariant that states that "A Basket can have no more than three Products".
- It is a central part in DDD.
- A specific responsibility enforced by explicit boundaries. These boundaries are set by the different way we represent models. Different contexts may have completely different models of common concepts with mechanisms to map between them.
- It gives team members a clear and shared understanding of what has to be consistent and what can develop independently.
- A Bounded Context can be considered as a miniature application, containing it’s own Domain, own code and persistence mechanisms.
- Within a Bounded Context, there should be logical consistency; each Bounded Context should be independent of any other Bounded Context.
- Communication to and from a Bounded Context is done via a Context Map.
- Entities are objects that are defined by their identity, and that identity continues through time.
- A unique thing that has a life cycle and can change state. For example, a conference in a conference management system will be an entity; many of its attributes could change over time (such as its status, size, and even name), but within the system each particular conference will have its own unique identity.
- Not all objects are defined by their identity. For some objects what is important are the values of their attributes. For example, within our conference management system we do not need to assign an identity to every attendee's address (one reason is that all of the attendees from a particular organization may share the same address). All we are concerned with are the values of the attributes of an address: street, city, state, and so on.
- Value objects are usually immutable (unchanging over time).
- Sometimes in one context something is an entity while in another it is just a value object.
You cannot always model everything as an object. For example, in the conference management system it may make sense to model a third-party payment processing system as a service. The system can pass the parameters required by the payment processing service and then receive a result back from the service. Notice that a common characteristic of a service is that it is stateless (unlike entities and value objects).
Services are usually implemented as regular class libraries that expose a collection of stateless methods. A service in a DDD domain model is not a web service. It communicates aggregate roots, performs complex use cases, cross aggregates transaction. An operation offered as an interface that stands alone in the model, with no encapsulated state.
-Whereas entities, value objects, and services are terms for the elements that DDD uses to describe things in the domain model, the terms aggregate and aggregate root relate specifically to the lifecycle and grouping of those elements.
- Aggregate is a cluster of domain objects that can be treated as a single unit to provide a specific functionality and for the purpose of data changes.
- An Aggregate has no getters, it represents its state by recording events. We use a Value Object to identify an Aggregate.
- Aggregate is not represented by its state, but by its history of Domain Events.
- Aggregates define consistency boundaries for groups of related entities; therefore, you can use an event raised by an aggregate to notify interested parties that a transaction (consistent set of updates) has taken place on that group of entities.
- Every aggregate has a unique ID; therefore, you can use that ID to record which aggregate in the system was the source of a particular event.
- Each aggregate has its own stream of events. Taken together, they can be used to compute its current state. Aggregates are completely isolated from each other.
- The first way to tell if your aggregate is right, is that you can look at each of the entities within it and ask, "Does this need to be directly accessed?" If you answer yes, then that entity is likely not a part of the aggregate.
- In summary, aggregates are the mechanism that DDD uses to manage the complex set of relationships that typically exist between the many entities and value objects in a typical domain model.
- An aggregate root (also known as a root entity) is the gatekeeper object for the aggregate. All access to the objects within the aggregate must occur through the aggregate root; external entities are only permitted to hold a reference to the aggregate root, and all invariants should be checked by the aggregate root.
- An aggregate root is an entity that composes other entities (as well as its own values) by composition. It is the domain’s only entry point for data access. A heart of your domain.
- The job of an Aggregate Root is to control and encapsulate access to it’s members in such a way as to protect its invariants. Any references from outside the aggregate should only go to the aggregate root. The root can thus ensure the integrity of the aggregate as a whole.
- Another DDD principle is that an aggregate root is responsible for ensuring that the aggregated entities are always in a valid state.
- Another area where there can be confusion is in distinguishing entities from aggregates. Every aggregate has an entity acting as its aggregate root, and for lots and lots of entities the aggregate will consist of just this entity. The point is that "Customer have Orders" does not mean imply aggregation; Customer, Order and Product are all aggregate roots.
- A Domain Event is something that happened in the past, and that is of interest to the business.
- Domain Events is an immutable array of Domain Event objects. Once they have been initialized, there's no way to change them, there are no setters. Because events happen in the past, they cannot be changed or undone. That makes perfect sense: History can't be altered!
- Events happen in the past. For example, "the speaker was booked," "the seat was reserved," "the cash was dispensed."
- Events are notifications; they report something that has already happened to other interested parties. For example, "the customer's credit card has been billed $200" or "ten seats have been booked for conference X." Events can be processed multiple times, by multiple consumers.
- Here are sets of events we may come up with from the cafe tab scenario. TabOpened, DrinksOrdered, FoodOrdered, FoodCancelled, DrinksServed, FoodPrepared, FoodServed, TabClosed.
- Note that the events are very domain-focused.
CQRS — Command-Query Responsibility Segregation
- A common sense rather than a pattern. CQRS just separates a model into two separate parts — READ model and WRITE model.
- The separation occurs based upon whether the methods are a command or a query.
- They also can be referenced as Query model and Command model. Segregation must be clean so commands can’t return any data.
- A command is any method that mutates state and a query is any method that returns a value.
- CQRS is a simple pattern that strictly segregates the responsibility of handling command input into an autonomous system from the responsibility of handling side-effect-free query/read access on the same system.
- CQRS is not a top-level architecture. CQRS is something that happens at a much lower level.
- Commands are imperatives; they are requests for the system to perform a task or action. For example, "book two places for conference X" or "allocate speaker Y to room Z." Commands are usually processed just once, by a single recipient.
- Commands are things that indicate requests to our domain.
- A command may be accepted or rejected.
- An accepted command leads to zero or more events being emitted to incorporate new facts into the system.
- A rejected command leads to some kind of exception.
- Commands are also identified by looking for verbs. For example OpenTab, PlaceOrder, MarkFoodServed, CloseTab etc. However, they are focused around what the user considers as an operation.
- An important part of the modeling process is thinking about the things that can cause a command to be refused. We are expected to model these "sad paths" into exception types, just as commands and events are expressed as DTOs.
- Looking to the cafe domain scenario, we can identify three notable exceptions we need in our model: CannotCancelServedItem, TabHasUnservedItems and MustPayEnough. The names here try to explain why the command failed.
- The idea of a command bus is that you create command objects that represent what you want your application to do. Then, you toss it into the bus and the bus makes sure that the command object gets to where it needs to go.
- So, the command goes in -> the bus hands it off to a handler -> and then the handler actually does the job. The command essentially represents a method call to your service layer.
- The Command Bus pattern relies on 3 types of classes: Command (encapsulate our input, does simple validation on it), CommandHandler (dedicated to a single Command, does the actual logic) and CommandBus (interface allowing us to build Middlewares that calls the appropriate CommandHandle for the given Command)
— an interpreter, which is a structure of objects which can form itself into an SQL query. You can create this query by referring to classes and fields rather than tables and columns. In this way, those who write the queries can do so independently of the database schema and changes to the schema can be localized in a single place.
- Event sourcing is a way of persisting your application's state by storing the history that determines the current state of your application. For example, a conference management system needs to track the number of completed bookings for a conference so it can check whether there are still seats available when someone tries to make a new booking.
- ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself.
- A method of storing business data as a sequence of modification events.
- mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
- A mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects.
- pretends to be a collection of Aggregate Roots.