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:

  1. Resource isolation for different service scenarios can be optimized
  2. For high THROUGHPUT at the C-side, the capacity can be expanded and shrunk infinitely through queues, and resources can be saved
  3. 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:

  1. Batch commit events and identify duplicate commands and versions
  2. Lookup by aggregating the root ID and commandId
  3. Query by aggregating the root ID and version
  4. 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.