Domain Services, Domain Events

    Combining the first two summaries, this one sorts out domain services and domain events. First note, domain services and application services. SOA services, or RPC calls between applications, Restful interfaces, or interaction between systems through message middleware can all be classified as application services. In contrast, domain services do not necessarily involve remote calls or heavyweight transaction operations. Therefore, context integration also involves how to divide bounded contexts, how to design to minimize the coupling of application services, and the anti-corrosion of application services to local models.

Domain Services

concept 

Borrow the definition of domain service in Implementing Domain-driven Design. When a certain operation process or transformation process of the domain is not the responsibility of the entity or value object, the operation should be placed in a separate interface, the domain service, and it should be consistent with the common language. There are many cases of non-entity or value object operations here. For example, an operation needs to operate on multiple domain objects and output a value object. In a layered architecture, somewhat similar to Manager. However, if the abstract manager is transitioned, anemia will occur, so you also need to ensure that the domain service is stateless, and make a good trade-off with the anemia model. In most cases, the parameters of the domain service are smaller than the actual domain model, and only have value objects for some key properties. If the service only operates on the entities or value objects of the domain, it can be considered to operate in the domain model.   

The Manager was mentioned earlier, but in many applications, the Manager is abstracted into the form of an interface, but in most cases, it is completely unnecessary. It can be decoupled by the service Factory, or the real service implementation class can be injected with Spring's @Service annotation. . For some simple domain operations, a mini-layer can also be abstracted. This mini-layer can also be called a domain service, which is just a stateless, non-transactional, and secure abstraction layer.     

In fact, domain events can also be summarized as domain services, but the events of domain services are idempotent. Because domain services are non-transactional, events are also side-effect free, so when processing aggregate dependencies, their eventual consistency needs to be guaranteed.  

Domain events

Model the activities that take place in the domain as a series of discrete events, each represented by a domain object. Simply put, domain events are events that occur in the domain. Take renting books as an example. If a book is borrowed, a book loan order needs to be generated, and for book renters, they need to be able to view the list and details of their rented books, and the book also needs to be checked. A state marked as no longer available to lend (because it has already been borrowed). Here bookRent can be issued as a domain event.  

Aggregation of events

For the above event model, we can create domain events with aggregate properties. Here we can model the event itself as an aggregate (BookRentEvent object) and have its own persistence method. The unique identifier can be determined by a set of properties. The domain event {new bookRentEvent())} is created when the client side (Client) calls the domain service, and added to the repository, and then published through a message. After the release is successful, call back to update the time status. However, it should be noted here that it is best to publish the message in the same context as the event repository, or share the data source, so that the successful submission of the event can be guaranteed. In different context systems, global transactions are required to ensure that. The role of the unique identifier here is to prevent message retransmission or repeated processing. So subscribers need to check for duplicate messages and ignore them. If it is a local context event, it is better to provide equals and hashcode implementation.   

Combined with the previous example, in the context of book management, the book is borrowed, then the unique representation of the book and the state of the book (Rent is checked out) can identify an event. This event needs to have the borrower's information (such as id, nick, etc.), then after persisting this event, you can post a local eventbus message, which is monitored by the user book domain service, and a series of operations such as updating the user book list. Then Callback to the event source, update the event status, and the processing is successful. If you need to handle events in the local context, it is not troublesome to handle.

Post Domain Events

The publication of domain events can use the Observer pattern. In the local context, it is also necessary to minimize the exposure of the domain model to the infrastructure or message middleware. Therefore, it is necessary to encapsulate the local model (domain model) into an event aggregation. For example, we cannot directly publish a BookRent aggregated event, but a BookRentEvent. This Event object will also hold some event-specific properties, such as occurTime (occurrence time) and isConsumed (whether it has been processed), as needed. . When an event is published, all subscribers will be notified synchronously. The main components of domain events are publishers and subscribers.   

sender

The sender itself does not express a domain concept, but as a form of service. No matter what technology is used to implement, what framework is used, the idea of ​​handling event sending may be different. For example, in a web application, the event registration of the subscriber to the sender can be handled when the application is started (to avoid the thread synchronization problem of registering and processing the sender). For example, events of interest can be registered to a local ThreadLocal publisher List. After the application is started, it can send an aggregate of events when it starts to process domain events. The aggregate of this event is an event object, not an entity in the domain model, because we want to expose the events that need to be exposed to other contexts, rather than exposing the complete domain object. If we use EventBus, we can encapsulate an event as a parameter when we post.  

subscriber

A subscriber to an event can be an independent component of an application service. Because the application service is at the outer layer of the domain logic, if it is purely event-driven, then the subscriber, as an application service, can also be positioned as an application service component with a single responsibility and responsible for event storage.

Distributed Domain Events

In dealing with distributed events, the most important and most difficult thing to deal with is consistency. The delay of the message and the idempotency of the processing will affect the accuracy of the domain model state and the processing of the event. But we can use some technical ways to achieve eventual consistency in the process of interaction between systems. This may require persistence of the event model. processing can be

1. The domain model and the messaging facility share a persistently stored data source. This need event is stored as a local event model in the same database as the local domain model. This ensures the consistency of local transactions and better performance, but cannot share persistent storage with other contexts.

2. Global XA transaction (two-phase commit) to control. Model and message persistence can be separated, but global transaction performance is poor and the cost is high.

3. In the persistent storage of the domain model, a separate storage area (a separate event table) is used to store domain events. That is to do a local EventStore. But there needs to be a message mechanism for publishing events, and message events are completely private. The sending of messages can be handed over to the message middleware for processing. Also store the time as a Rest resource if you can. Events can then be published externally in the form of an archive log (message queues, sending RabitMQ, MetaQ, etc. via message facilities or middleware). This also ensures traceability of time.    

 

We use events for decoupling in order to avoid RPC as much as possible, simplify system dependencies, and reduce the state impact of the unavailability of external services on the system model. Therefore, domain events emphasize a high degree of autonomy, but it also needs to be considered. The case of event processing must be tolerant to delay, and the receiver of the message needs to be an idempotent receiver (it can be self-idempotent, or refuse to repeat messages). processing), since messages may be sent repeatedly.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327055515&siteId=291194637