In the process of implementing DDD, it is easy to get caught up in the concepts and write boilerplate code that meets the corresponding layers. Define aggregation, entity and value objects, and then have a Repository for aggregation operations. When aggregation is more complex, you also need a Factory. After that, the concept of domain service comes up, which is the most confusing. Domain services are the product of a compromise that really doesn’t know which aggregation to put in. It’s better not to use domain services because they shouldn’t exist. At the same time, traditional DDD in the practice process of increasing read and write amplification, how to optimize the database for multiple operations, has not given a good solution.
To sum up: I think this is also a very big threshold for everyone to practice
CQRS myth
In recent years, the concept is one of the biggest influence on my CQRS, in the design of various business scenarios, there will be more or less similar design, compared with the traditional CRUD, CQ is a kind of thinking, to meet the business scenario, will take the initiative to identify the Command and the Query, because the two face to solve the problem domain is not the same, the existing storage system, Read and write are almost impossible to have at the same time, so we have heterogeneous data sources, a lot of customization for read libraries, and write models that guarantee good performance, stability and scalability. I think the idea of CQRS is a reasonable solution under the existing system
EventSourcing
Event tracing is a concept that is difficult to be implemented. In short and fast Internet applications, it is undoubtedly a burden to record the events of each operation
Final consistency
When we take the traditional DDD architecture approach, in order to write ddD-compliant code, the implementation of complex technology should consider whether it is compatible with this writing method, in order to achieve the final convergence between the aggregation, how do we do distributed transactions
DDD application framework
Enode provides a solution to this problem that is not quite the same as Axon’s solution
The overall architecture
This is the architecture diagram of the Enode
Use the constraints
- A command modifies only one aggregate root at a time
- Aggregations can only interact with each other through domain messages
- Strong consistency within polymerization
- Final consistency between aggregates
The core idea
A command modifies only one aggregate root at a time
First of all, this restriction is considered from the perspective of business research and development, which will make the responsibility of the command more specific and facilitate problem decomposition and responsibility division. If a command needs to modify multiple aggregation roots, it should be completed through Saga (Process Manage)
Plus the benefits of this agreement:
- Command operations from the same aggregate root are routed to the same partition, so the aggregate root can live In in-memory, so that the aggregate root does not have to be rebuilt every time. The cache utilization aggregate is 100%, which is a design that maximizes Memory utilization
- Commands are routed to the same partition, and the order of operations of commands is guaranteed (commands carry versions of the aggregate root). This ensures that only one aggregate root is operating at a time, directly avoiding concurrency problems because it is designed to be unlocked
- As for the guarantee of order of command operation, in order to improve throughput, the queue is required to be disordered consumption, but how to ensure orderly operation if the queue is disordered? This is similar to the design of Watermarker in Flink. The mailbox of the aggregation root records the version of each message, and if the data of the higher version arrives first, the data will be stored temporarily. Wait until the processing of the intermediate version is completed, which ensures the order of the operation through the order in the Mailbox
Infrastructure depend on
Distributed message queue
There are three main reasons for relying on queues:
- Resource isolation for different service scenarios can be optimized
- For high THROUGHPUT at the C-side, the capacity can be expanded and shrunk infinitely through queues, and resources can be saved
- To route the same aggregation root to the same consumer, the aggregation reconstruction is reduced and the cache utilization is high
EventStore
For EventSourcing, there is always a place for event_stream, and the aggregate root consumption progress (PUBLisheD_version) defines the capabilities to be provided:
- Batch commit events and identify duplicate commands and versions
- Lookup by aggregating the root ID and commandId
- Query by aggregating the root ID and version
- Lookup by aggregating the root ID and the minimum maximum version range
At the same time, the extension is left, you can choose the implementation independently, currently provide the default implementation (MySQL, PG, MongoDB)
Programming model
Event-driven myths:
-
When to be event-driven and when to use procedural programming?
-
The difference between a command and an event, both are messages, why represent them separately?
My understanding is as follows,
Orders can be refused. The event has occurred.
This is probably the most important reason. In an event-driven architecture, there is no doubt that the events raised are representative of what happened.
Now, because commands are things that we want to happen, and events have already happened, we should use different words when we name these things, commands are usually nouns, and events are usually past participles
For example, taking the order system, we have a dependency on an external payment system.
When the user finishes paying in the payment system, the payment system sends a Command to the order system, MarkOrderAsPayed. When the order processes this Command, it retrieves the current order, calls the order’s mark paid (action), The OrderPayed event is generated.
As you can see, commands are usually called from outside the system, and events are provided by handlers and other code in the system.
That’s another reason why they’re represented separately. Conceptual clarity.
Commands and events are messages. But they are really separate concepts, and concepts should be explicitly modeled.
I understand the two are in conformity with the human mind, the first is based on the brain receives the perceived message (Event) produce an idea “intent” (Command), and then how to implement this idea, thinking is procedural dimension, in the process of implementation, will produce some Event message, the message will affect the brain. And so on.
Storm events
Just a few words about the experience of event storms
- Start with the use-case dimension
- Every use case starts with an end
- Enumerating the main process first, then adding exception handling, MECE, logical closed loop, exhaust
- Add actor and command information
- Finally, add Policy
Saga implementation
Saga can be implemented in two modes, one is control and the other is choreography. Through event messages, business does not need to care about the process of technical implementation at all. By communicating with business experts about requirements and event storms, drawing command events, the logic will be immediately clear. Observability can also be done well through the system. Meanwhile, Saga has more advantages in performance with the distributed transaction model, which is essentially different from Axon’s Unit of Work.