Domain Driven Design (DDD): Domain Event Handlers – Where to place them? - domain-driven-design

I am confused about where to handle domain events in an application that is based on the hexagonal architecture. I am talking about the bounded-context-internal domain events, and not about inter-context integration/application/public events.
Background
As far as I understand, application logic (i.e. use case logic, workflow logic, interaction with infrastructure etc.) is where command handlers belong, because they are specific to a certain application design and/or UI design. Command handlers then call into the domain layer, where all the domain logic resides (domain services, aggregates, domain events). The domain layer should be independent of specific application workflows and/or UI design.
In many resources (blogs, books) I find that people implement domain event handlers in the application layer, similar to command handlers. This is because the handling of a domain event should be done in its own transaction. And since it could influence other aggregates, these aggregates must be loaded via infrastructure first. The key point however, is this: The domain event is torn apart and turned into a series of method calls to aggregates. This important translation resides in the application layer only.
Question
I consider the knowledge about what domain events cause what effects on other aggregates as an integral part of the domain knowledge itself. If I were to delete everything except my domain layer, shouldn't that knowledge be retained somewhere? In my view, we should place domain event handlers directly in the domain layer itself:
They could be domain services which receive both a domain event and an aggregate that might be affected by it, and transform the domain event into one or many method calls.
They could be methods on aggregates themselves which directly consume the entire domain event (i.e. the signature contains the domain event type) and do whatever they want with it.
Of course, in order to load the affected aggregate, we still need a corresponding handler in the application layer. This handler only starts a new transaction, loads the interested aggregate and calls into the domain layer.
Since I have never seen this mentioned anywhere, I wonder if I got something wrong about DDD, domain events or the difference between application layer and domain layer.
EDIT: Examples
Let's start with this commonly used approach:
// in application layer service (called by adapter)
public void HandleDomainEvent(OrderCreatedDomainEvent event) {
var restaurant = this.restaurantRepository.getByOrderKind(event.kind);
restaurant.prepareMeal(); // Translate the event into a (very different) command - I consider this important business knowledge that now is only in the application layer.
this.mailService.notifyStakeholders();
}
How about this one instead?
// in application layer service (called by adapter)
public void HandleDomainEvent(OrderCreatedDomainEvent event) {
var restaurant = this.restaurantRepository.getByOrderKind(event.kind);
this.restaurantDomainService.HandleDomainEvent(event, restaurant);
this.mailService.notifyStakeholders();
}
// in domain layer handler (called by above)
public void HandleDomainEvent(OrderCreatedDomainEvent event, Restaurant restaurant) {
restaurant.prepareMeal(); // Now this translation knowledge (call it policy) is preserved in only the domain layer.
}

The problem with most even handler classes is that they often are tied to a specific messaging technology and therefore often placed in the infrastructure layer.
However, nothing prevents you to write technology-agnostic handlers and use technology-aware adapters that dispatches to them.
For instance, in one application I've built I had the concept of an Action Required Policy. The policy drove the assignment/un-assignment of a given Work Item to a special workload bucket whenever the policy rule was satisfied/unsatisfied. The policy had to be re-evaluated in many scenarios such as when documents were attached to the Work Item, when the Work Item was assigned, when an external status flag was granted, etc.
I ended up creating an ActionRequiredPolicy class in the domain which had event handling methods such as void when(CaseAssigned event) and I had an even handler in the infrastructure layer that simply informed the policy.
I think another reason people put these in the infrastructure or application layers is that often the policies react to events by triggering new commands. Sometimes that approach feels natural, but some other times you want want to make it explicit that an action must occur in response to an event and otherwise can't happen: translating events to commands makes it less explicit.
Here's an older question I asked related to that.

Your description sounds very much like event-sourcing.
If event-sourcing (the state of an aggregate is solely derived from the domain events), then the event handler is in the domain layer, and in fact the general tendency would be to have a port/adapter/anti-corruption-layer emit commands; the command-handler for an aggregate then (if necessary) uses the event handler to derive the state of the aggregate, then based on the state and the command emits events which are persisted so that the event handler can derive the next state. Note that here, the event handler definitely belongs in the domain layer and the command handler likely does, too.
More general event driven approaches, to my mind, tend to implicitly utilize the fact that one side's event is very often another side's command.
It's worth noting that an event is in some sense often just a reified method call on an aggregate.

I follow this strategy for managing domain events:
First of all, it is good to persist them in an event store, so that you have consistence between the fact that triggered the event (for example, a user was created) and the actions it triggers (for example, send an email to the user).
Assuming we have a command bus:
I put a decorator around it that persists the events generated by the command.
A worker processes the event store and publish the events outside the bounded context (BC).
Other BCs (or the same that published it) interested in the event, subscribe to it. The event handers are like command handlers, they belong to the application layer.
If you use hexagonal architecture, the hexagon is splitted into application layer and domain.

Related

Using infraestructure in use cases

I have been reading the book Patterns, principles and practices of domain driven design
, specifically the chapter dedicated to repositories, and in one of the code examples it uses infrastructure interfaces in the use cases, is it correct that the application layer has knowledge of infrastructure?, I thought that use cases should only have knowledge of the domain ...
Using interface to seperate from implementation is the right way, so use cases layer knows interfaces not infrastructure detail.
It is the Application Layer's responsibility to invoke the (injected) infrastructure services, call the domain layer methods, and persist/load necessary data for the business logic to be executed. The domain layer is unconcerned about how data is persisted or loaded, yes, but the application layer makes it possible to use the business logic defined in the domain layer.
You would probably have three layers that operate on any request: A Controller that accepts the request and knows which application layer method to invoke, an Application Server that knows what data to load and which domain layer method to invoke, and the Domain Entity (usually an Aggregate) that encloses the business logic (a.k.a Invariants).
The Controller's responsibility is only to gather the request params (gather user input in your case), ensure authentication (if needed), and then make the call to the Application Service method.
Application Services are direct clients of the domain model and act as intermediaries to coordinate between the external world and the domain layer. They are responsible for handling infrastructure concerns like ID Generation, Transaction Management, Encryption, etc.
Let's take the example of an imaginary MessageSender Application Service. Here is an example control flow:
API sends the request with conversation_id, user_id (author), and message.
Application Service loads Conversation from the database. If the Conversation ID is valid, and the author can participate in this conversation (these are invariants), you invoke a send method on the Conversation object.
The Conversation object adds the message to its own data, runs its business logic, and decides which users to send it to.
The Conversation object raises events to be dispatched into a message interface (collected in a temporary variable valid for that session) and returns. These events contain the entire data to reconstruct details of the message (timestamps, audit log, etc.) and don't just cater to what is pushed out to the receiver later.
The Application Service persists the updated Conversation object and dispatches all events raised during the recent processing.
A subscriber listening for the event gathers it, constructs the message in the right format (picking only the data it needs from the event), and performs the actual push to the receiver.
So you see the interplay between Application Services and Domain Objects is what makes it possible to use the Domain in the first place. With this structure, you also have a good implementation of the Open-Closed Principle.
Your Conversation object changes only if you are changing business logic (like who should receive the message).
Your Application service will seldom change because it simply loads and persists Conversation objects and publishes any raised events to the message broker.
Your Subscriber logic changes only if you are pushing additional data to the receiver.

How domain events can be communicated (published / subscribed) across bounded context

An open host service is a way of mapping between contexts that is appropriate for cases where you would expose access via APIs. What is a good way to represent a mapping beween contexts, where you intend to publish / consume domain events?
How to interact across bounded context when domain events occurs in the system ?
Interacting via events is a very powerful way to develop the application. Once you get used to this you will feel very easy and convenient way to interact within and across systems (depending on the business needs).
Let us consider you have two bounded context context-A and context-B respectively. In bounded context-A based on certain business logic in domain model you want to notify the other bounded context-B to take certain action.
In this case after executing your business logic in domain model. You can create an domain event from your domain service and let your application service (which is local to your bounded context in this case context-A) manage to fire an Domain Event to the publisher with help of your infrastructure layer. Now the publisher can store this event in the Event Store and then forward to the Messaging Queue.
The Subscriber in bounded context-B can make arrangements to listen to the events on bus and take required action to be executed.
Thus how in domain driven design we can organise to publish or subscribe to the domain events.
If bounded context is in two different code base/projects its always good to use Event store and Message queue
If its in the same code base you can manage to common publisher and subscriber which helps to mange domain events.
Hope this high level explanation may help.

Considering Axon in greenfield project

I'll be starting on a greenfield project in a few months.
The project will contain lot's of business logic, spread over several subdomains. Yes, we'll be using principles of Domain Driven Design.
Tech will consist of Spring, Spring Boot & Hibernate stack.
I was looking after some Java libs to cover infrastructural things like:
domain event publication
event store
event deduplication
resequencers on consumer side
projections
reliable publishing
reliable delivery & redelivery
...
I came across the Axon Framework. I already heard about it, didn't know it in details. So I read some blogposts, little bit of documentation and watched some broadcasts on Youtube.
It seems very promising, I'm considering to use it because I don't want to reinvent to wheel over and over again on the infrastructural side.
So I'm looking for someone to answer and clarify my questions:
Command handling
Axon use CommandHandlers with void methods. Is it possible to make them return a value (for instance a generated business id) or objects for notification purposes concerning the business operation? It's not a issue to me that the method will be I/O blocking by this.
Local vs remote domain events publication
I want to have a clear separation of local vs remote domain events.
Local domain events should only be visible and consumed to the local subdomain. Is it possible to configure event consumption sync and/or async?
My Local domain events can be 'fat'. They are allowed to carry more data because it won't cross the domain boundaries.
Remote domain events will be 'thin', so only the minimum data necessary for remote domains. This type op events need always to be handles async.
Is it possible to convert a local (fat) domain event to a remote (thin) domain event at the edge of a domain? By 'edge', I mean the infrastructural side.
By this, the domain model doesn't need to know distinction between local & remote domain events.
CQRS synchronously
My application will consist of 1 (maybe 2) core domains and several subdomains. Some domains contain lot's of business logic and will require CQRS.
Other domain will be more 'crudy' style.
Is it possible to do CQRS synchronously? I want to start this way before adding technical complexities like async handling. It this plossible with Axon?
This also means that domain events will be stored in a events store without using event sourcing. Can Axon's event store be used without event sourcing?
Same for projection stuff, I just want to projection domain events to build my read model.
Modular monolith
We'll use a modular monolith.
Not very trendy these days with all the microservices stuff. Although, I'm convinced of having a monolith where each domain is completely separated (application code & DB-schema), where operations will be handled with eventual consistency and domain events contain the necessary data.
Later on, and if necessary, it will be easier to migrate to a microservices architecture.
Is Axon a framework that fits in a modular monolith kind of architecture? Is there anything to take into account?
Fully separated domain model (persistence agnostic)
The domain model will be completely separated from the data model.
We need to have a repository that reads a data model (using Hibernate) and uses a data mapper to create an aggregate when it needs to be loaded.
The other way is also needed, an aggregate needs to be converted and saved into the data model (using data mapper).
Additionally, the aggregates's domain events need to be stored into an event store and published to local or remote event handlers.
This has some consequences:
we need to have full control of repository implementation that communicates with one or more DAO's (Spring data repositories) to take the necessary data out of Hibernate entities and construct an aggregate with it. An aggregate might be modeled in 2 or even 3 relational tables after all.
we don't need any Hibernate annotation in the domain model
Is this approach possible with Axon? I only see examples using direct JPA (domain model maps 1 to 1 to entities) or event sourcing.
This approach is really a deal breaker for us, a separated domain model gives so much more possibilities than mapping it directly to data entities.
Below an example of what I want to achieve:
Aggregate (without JPA) in some domain model package:
public class ScoringResultAggregate {
// members, constructor, operation omitted for brevity
}
Hibernate Entity in some infrastructure package:
#Entity
#Table(name ="SOME_TABLE_NAME)
public class ScoringResultEntity {
// member and getters & setters; no domain logic
}
Repository interface that belongs to the domain model:
public interface ScoringResultRepository {
void save(ScoringResultAggregate scoringResultAggregate);
ScoringResultAggregate findByApplicationNumber(ApplicationNumber applicationNumber);
}
Adapter that implements repository interface; responsible for mapping aggregate from/to data (JPA) model:
class ScoringResultAdapterRepository implements ScoringResultRepository {
private ScoringResultJpaRepository scoringResultJpaRepository;
ScoringResultJPARepository(ScoringResultJpaRepository scoringResultJpaRepository) {
this.scoringResultJpaRepository= scoringResultJpaRepository;
public void save(ScoringResultAggregate scoringResultAggregate) {
// converts aggregate to ScoringResultEntity and saves the state into DB
}
public ScoringResultAggregate findByApplicationNumber(ApplicationNumber applicationNumber) {
// loads an ScoringResultEntity from DB and converts it into an aggregate
}
}
Axon Server
Axon server looks very promising. Although, is it only useful for event sourcing?
Can it be used together with a Sql DB where aggregates are stored (state persistence) and domain events get persisted in Axon Server?
Lot of questions. Hopefully, someone with Axon experience can help me out :-)
I feel Jasper is saying the right things, but I also think I can emphasize them a little more:
Command handling - Yes you can have return values on command handlers. Just be mindful that you do no abuse this to return state of the to the user, as that would be mixing the Command Model (your Aggregate handling the command) with your Query Model.
Local vs remote domain events publication - Jasper states this clearly and he's right. Your hitting the desire to form bounded context's, for which Axon Server (Enterprise) has support. If you'd not use Axon, you'll have to build this infrastructure yourself.
CQRS synchronously - Axon provides handles for asynchronous and synchronous messaging just fine. The main difference is that you'll block on the result of sending your messages. The CommandGateway for example has a send and sendAndWait method, thus providing you with sync and async command dispatching. Lastly, it's perfectly fine to use Axon Server as the event store without doing Event Sourcing. Event Sourcing is an choice when using Axon, not a requirement.
Modular monolith - AxonIQ as a company actively encourages this approach to building software. So yes, you can do this, and no, I cannot think of anything you should think of prior to doing this.
Fully separated domain model (persistence agnostic) - From your Query Model you have full control over how you'd want to map your data model to and from the actual model you'd use. The Aggregate in Axon terms should be regarded as your Command Model, for which you can choose the Event Sourced storage approach or the State Stored storage approach. The state-stored implementation given by Axon Framework works based on JPA, which would thus require you to set some annotations along side the axon annotations in your Command Model. If you need to segregate this, I could imagine you'd create your own variant of the Repository and AggregateFactory. Then again, the Event Sourcing approach would make your domain model clear of persistence annotations altogether, so I'd go for that route to be honest.
Axon Server - Yes you can use Axon Server even if you go the state-stored approach for Aggregates. Know that Axon Server next to being an event store is a unified routing solution for commands, events and queries. If you would move from a modular monolith to a (micro) services set up, having Axon Server in place to perform all the message routing will make your life very, very easy.
I hope I can answer some of them, but I'm also not really experienced in using Axon:
Return values from command handler - Yes, thats possible. We had an example where we return the generated aggregate id (I'm not 100% sure about this answer)
Local vs remote domain events publication - Yes, Axon Server ENTERPRISE (!) supports multi-context thats build for this purpose. https://axoniq.io/product-overview/axon-server-enterprise
CQRS synchronously - The question is not totally clear but it's not necessary to model your complete system with CQRS. You can use CQRS for some domains and other architecture for subdomains.
Use Saga's for any kind of "transaction" like stuff. Rollbacks should be written by the developer. The system can't do this for you.
Modular monolith - Shouldn't be a technical problem.
Fully separated domain model (persistence agnostic) - The question is not totally clear but store only events in Axon Server. Aggregates are build up by a sequence of aggregates. Don't use any other data for it. The aggregate are used to do the command handling with state checks and apply new events.
I a system gets a command message, Axon Framework will look at the aggregate id and re-creates the aggregate by replay all the existing events for that aggregate. Then the method for #CommandHandler and command message type is called on the aggregate with the state of the system. Don't do this by yourself.
On the other hand. Create own custom projections (view models) by listening to the events (#EventHandler) and store the data in your own format to any kind of data models/repository. You can for example build a REST api on top of this to use the data.
Axon Server - Use it where it's built for. Use it as event store and not for other purposes.
See for more info and why: https://www.youtube.com/watch?v=zUSWsJteRfw

#Service injection into aggregates?

I have an Order aggregate with the following commands:
CreateOrderCommand
PlaceOrderCommand
... (rest redacted as they are not pertinent to the question) ...
The PlaceOrderCommand refers to placing the Order onto an external execution venue. I have captured the behaviour for placing an order onto an external execution venue within a separate (non CQRS) #Service. However, I am struggling (due to lack of experience with Axon) with how best to connect my #Service with the aggregate.
My normal way of thinking would have me:
Inject the #Service into the aggregate's #Autowired constructor.
When the PlaceOrderCommand is issued, use the service to place the order onto the relevant execution venue and once done emit an event (either OrderPlacedSuccessfullyEvent or ErrorInOrderPlacementEvent).
Change the aggregate's state within the relevant #EventSourcingHandler.
My question is:
does my description above of how to handle this particular use case with Axon make sense (in particular injecting a #Service into an aggregate feels a bit off to me)?
Or is there a different best practice of how to model my scenario when using CQRS/event sourcing with Axon?
Axon requires an empty constructor in the aggregate. How does this reconcile with having the #Autowired constructor?
The other thing I was potentially considering was:
having a PlaceOrderInstructionCommand (instead of the simple PlaceOrderCommand) which emits a ReceivedPlaceOrderInstructionEvent that a separate event listener is listening for.
that event listener would have the relevant #Service injected into it and would do the placement of the Order.
then after placing the Order it would send a command (or should it emit an event?) to the aggregate informing it to update its state.
Could you please advise on what is best practice for modelling this scenario?
The PlaceOrderCommand refers to placing the Order onto an external execution venue.
I'm assuming that placing the Order to an external execution venue means interacting with an external system. If yes, then it is should not be part of your domain. In that case, you would need to raise an Integration Event.
As you mentioned, you could raise a Command like ProcessOrder from your Domain. Within that Command, you can update your Domain (eg, set the OrderStatus to Processing) and raise an integration event like OrderArrived which is then handled by a separate process.
From Microsoft Docs:
The purpose of integration events is to propagate committed transactions and updates to additional subsystems, whether they are other microservices, Bounded Contexts or even external applications.
Integration events must be based on asynchronous communication between multiple microservices (other Bounded Contexts) or even external systems/applications.
You would handle that integration event as a separate process (or worker) outside of your Domain. This is where your#Service would be injected. Once, the order is processed successfully you can then broadcast integration event called OrderPlaced.
Now, any subscriber which has anything to do with placing the order would subscribe to the event. In your case, your Domain is interested in updating the state once the order is placed. Hence, you would would Subscribe to OrderPlaced event within your Domain to update the status of the Order.
Hope it helps.

DDD: Domain Events implementation in monolithic application

I've made some small research about Domain Events, and have found few different solutions
Udi Dahan solution, which handle events immediately
Deferred domain events, which fire off in infrastructure mostly
Domain Events which return result
Questions:
Which one is a pure Domain Events ?
Is it possible to have them all in the same project ?
In that case how should I name and distinguish them ?
Where to register EventHandler ? Someone mentioned that Application Service is appropriated place, but here I've seen that it was registered right into the Domain Model, and handled there, as well, and not in separate Event handler class.
One more extra question.
For example: When order is created and paid it has to get status "OrderPaid".
Because purchasing and ordering are two different contexts, right after Order was created we need to rise a Domain Event, which should be handled by Event Handler in Purchasing bounded context, but in result of Event Handling, there should be raised one more Domain Event - OrderPaid, which might be handled by Order context again. With monolith application it seems that one solution might be: pass Order object into Event Handlers to achieve expected behavior. Is there any other ways how to solve it, in such architecture style ?
Domain Events were invented to provide a more encapsulated domain model. There's no pure implementation as such, just different implementations with different trade-offs. You can have your events in the same project and there is plenty of guidance in the naming of events in the articles you've referenced.
If you wish to handle long running processes that are eventually consistent between different bounded contexts, I'd probably look into using a shared message bus like NServiceBus or MassTransit.

Resources