Why is EventSourcingHandler (in aggregate object) needed?
Asked Answered
C

1

8

Fair warning: I have no idea what I'm doing, so even the asking of this question may go awry.

I'm wanting to update state on a simple object (the Aggregate) and then provide the UI with a projection of the changed object. Here's my aggregate object (command handler exists, but not shown here).

@Aggregate
public class Widget {
    @AggregateIdentifier
    private String id;
    private String color;
...
    @EventSourcingHandler
    public void on(ChangeColorEvt evt) {
        color = evt.getColor();
    }
... 
}

...and here is my projection:

public class WidgetProjection {
    private final EntityManager entityManager;
    private final QueryUpdateEmitter queryUpdateEmitter;
...
    @EventHandler
    public void on(ChangeColorEvt evt) {
        ProjectedWidget projection = entityManager.find(ProjectedWidget.class, evt.getId());
        projection.setColor(evt.getColor());
        queryUpdateEmitter.emit(FetchWidget.class, query -> evt.getId().startsWith(query.getFilter().getIdStartsWith()), projection);
    }
...
}

The QueryHandler does what you would expect, finding the instance and returning it.

So here are my questions:

  1. Why do I need the EventSourcingHandler in the Aggregate? That is, it appears to do work, but the result isn't stored or sent anywhere.
  2. So, that becomes my next question: After executing the EventSourcing Handler, is the resulting instance of Widget (not the projection) stored, seen, or sent anywhere?
  3. If EventSourcingHandler is indeed needed, is there any way to avoid having two independent copies of my business logic (one in EventSourcingHandler, and one EventHandler)? I really hate the idea of having to update the same business logic in two places.

I appreciate any clarity the group can provide. Thanks for your help!

Chas answered 28/2, 2019 at 20:25 Comment(2)
@Allard, I'm hoping you or someone from your team will see this. I appreciate any help. Thanks.Chas
And I can guarantee you, someone on his team has seen it. I hope I can clarify some things for you @Jonathan M :-)Pomander
P
10

I hope I can help you out with this question!

The short answer would be: The necessity of using the @EventSourcingHandler really depends on how you want to model your application.

Now, that doesn't say much, so let me elaborate a little why I am stating this. In answering your questions I am assuming that your desire is to create an application where you follow the ideas of DDD, CQRS and Event Sourcing. Thus, the concepts which Axon tries to simplify for you whilst building your application.

Let me go over your numbered questions from here:

  1. The reason you'd need @EventSourcingHandler annotated functions in your Aggregate, is to (1) update the Aggregate state based on the (2) events it has published. To go to point 1, why would you update the state of your Aggregate? As your Aggregate is the Command Model of your application, it is tasked with making the decisions based on the commands it receives. To be able to make decisions, sometimes an Aggregate needs state, but sometimes it does not. So in the Widget example you've shared, I'd assume that the color field is no used at all to drive business logic later on, hence you could perfectly omit this state from the Aggregate without any problems. With the second point of my response I try to point out that an Aggregate will only ever handle the events which originate from itself. This is as the events are the source of your Aggregate, as the events constitute all the delta's which have occurred on that given model.
  2. Your following questions fits nicely with proceeding the explanation I've started in point 1. The answer is quite simple, your Widget Aggregate is not stored anywhere, not as is. Every time you'd retrieve your Aggregate from the Repository (this is done automatically for you in Axon), which defaults to an EventSourcingRepository in Axon, all the Events for that given Aggregate will be retrieved from the Event Store. Than, an empty Aggregate instance is created and the framework will replay all the events it has found for that exact Aggregate instance. You're thus effectively Event Sourcing your Aggregate every time a new command comes in. This might sound a little overkill, as the number of events for a given Aggregate might grow to quite a large set. This can be solved by doing things like making snapshots of the Aggregate.
  3. If this form of splitting your application in a dedicated part which deals with your business logic, the Command Model, and the part which simply returns a Query Model as an answer, the Query Model, then you could decide to have a State Stored Aggregate. So note, your are not required, at all, to do Event Sourcing when using Axon; it's just the default modus operandi for the framework. Thus I understand the felt concern from your part, that you're duplicating your logic. You can however strictly separate the part which makes all the decisions to be held in your Aggregate. The Query Model (in your example the ProjectedWidget) can be stored in what ever format and in what ever database/tool you'd like, ideally without any business logic. If you do find yourself adding business logic in the Query side of your application, this might suggest you should upgrade this bit as an event originating from your Aggregate's.

I hope this brings you some insights into why you'd go for Event Sourcing to begin with. This article describes CQRS in a little more detail than I could do here, and this link for Event Sourcing; hope they might serve as a more thorough explanation than I just gave you.

Pomander answered 1/3, 2019 at 7:43 Comment(7)
Ah, so the state changes in the EventSourcingHandlers (ESH) are only made to enable decision logic in the CommandHandlers (CH), right? And, if an ESH state change will not be used in a latter CH decision, the ESH could/should be eliminated, right? Instead, the state should only change in the EventHandler (EH) in the query, right?Chas
Still, in a situation where an ESH is needed, the EH's state change is basically identical to it. If the state change involves multiple properties with calculations, there is an opportunity for error: perhaps the calculations are coded correctly in the ESH, but not in the EH. We've created a bad situation because we have essentially the same calculations, but in two places. Is there a way to avoid that?Chas
Hi Jonathan, the best way to avoid that, is by making important decisions and calculations in the command model, leaving only trivial calculations in the view models. Be careful not to overdo it, though. Modeling is a fine balancing act...Sourpuss
@JonathanM if you need to avoid duplication in respect to domain specific calculations, I think you could place those in a domain service. From my point of view I'd let that service take in all the required fields for the calculations and return the same result each time; maybe even go as far as to make it a abstract class with static functions for the calculations.Pomander
@Allard, ah, I think you're saying to put the heavy calculations in the CH and pass the result to the ESH/EH so those handlers can update state, right?Chas
FYI, the "snapshots" hyperlink in your bullet #2 above no longer works. Is the following the equivalent page in the new version of the docs today? docs.axoniq.io/reference-guide/v/4.6/axon-framework/tuning/…Halfhearted
That's the right one, @eriegz! And, thanks for notifying me about this. I've just adjusted the link in my reply to point to the right page.Pomander

© 2022 - 2024 — McMap. All rights reserved.