Parent and Child events

Hello all,

I have a situation where my controller uses the gateway to send a CreateParentCommand. The ParentAggregate constructor handles the command and applies the ParentCreatedEvent.

Now I need to handle that event somewhere else and use the gateway to send a CreateChildCommand (the ChildAggregate already has a handler constructor where the ChildCreatedEvent is applied).

I’m not sure what the best approach is for that, but here’s how I tried to handle it:

I created a CreateParentSaga that handles the ParentCreatedEvent and sends the CreateChildCommand through the autowired command gateway. The associationProperty on the Saga is set to the guid property of ParentCreatedEvent. I annotated this saga event handler as @StartSaga and @EndSaga.

It all works and the events get published, but I see that a separate Transaction being created for each of the 2 commands I am sending through the gateway, when I hoped only one transaction would be used.

I tried this using JPA default configuration as the Event Sourcing storage and I see two transactions being started.

I also tried it with my own implementation of the EventStore interface. In this case, I have control of the commit method that I override on my implementation and I can see it being hit 3 times. The first time with a collection of the 2 events that were applied and then 1 more time for each of the individual events. My storage engine is Greg Young’s Event Store and it doesn’t duplicate the events, so all is fine in that aspect. The default JPA implementation also doesn’t duplicate them for that matter (I’m assuming that’s because the sequence number is the same…).

But I still would like it if that commit was only fired once, taking in a collection with 2 events…
Maybe if the command bus used the same transaction for each command or the same unit of work the commit would only be fired once.
I’m not entirely sure, but I have the impression that the commit is fired 3 times because 3 units of work have been registered, 1 for the saga, 1 for the ParentAggregate and 1 for the ChidAggregate.

Any help would be appreciated,
Bruno

Looking closer, while debugging, the last 2 commits are both for the ChildCreatedEvent, so that event is committed on the first commit call (along with the ParentCreatedEvent) and then 2 more times on the following unwanted commit calls… :frowning:

Any help would still be appreciated :slight_smile:
Bruno

Hi Bruno,

note that a Unit of Work is not a transaction. You can attach a transaction to one, which you can commit when the unit of work commits (which is usually what happens when you configure a TransactionManager). You have 3 commits because you have 3 messages being handled.

Also, don’t use a Saga if you have only one method that creates the Saga and then deletes it. It’s a waste. Just use a “regular” event handler bean to handle that for you. Sagas only make sense when you have a long running business process to coordinate.

Hope this clarifies what’s going on.
Cheers,

Allard

Hi Allard, thanks for the quick reply.

I did try with the event handlers before using the saga and had the same outcome. Nevertheless, I will implement it with the event handlers instead.

I understand that the unit of work is not a transaction, but it appears that each command or event being published is creating it’s own… Not quite sure, but I see 2 units of work when debugging. But I get 3 commits… and I only have 2 events, not 3. But maybe a bit more context might help:

I am just relying on Axon auto configuration, so I am not really configuring my own TransactionManager. On my EventStore implementation, I am extending AbstractEventBus, so I get all of the UnitOfWork orchestration for free. All I had to implement was the actual commit method that is called from:

private List<EventMessage<?>> eventsQueue(UnitOfWork<?> unitOfWork) {
...
unitOfWork.onCommit(u -> {

    if (u.parent().isPresent() && !u.root().phase().isAfter(COMMIT)) {
        u.root().onCommit(w -> doWithEvents(this::commit, eventQueue));
    } else {
        doWithEvents(this::commit, eventQueue);
    }
});
...

}

Since I am applying 2 Events, the parent and the child, I see that eventsQueue is call once for each apply (in the publish method), which makes sense, since publish gets called on each apply. But then I would expect 2 commits, one per event, but I get 3.
Moreover, the first commit has the 2 messages in it. I thought each unit of work managed it’s own queue of events, so each commit would only contain one event. It seems that when the second event is applied it’s somehow added to the queue on the already existing UoW and also to another UoW that was created I’m guessing to handle this second event.
It’s also odd that after the first commit with the 2 events, the second event is committed twice.

I guess what I would like to understand is if there is a way that I can have only one UoW be created and used each time I send commands/apply messages, because I have a requirement that all events or none get committed.

Not sure if there is any other piece of info I could supply to help steer me the right way.

I was able to identify that when the event for the child entity is published, the AbstractEventBus checks if there is a parent UoW and adds the new event to it. It does the same for each new event. I have fired 3 child events. When the transaction reaches the commit fase, the parent UoW commits first with 4 events in this case. Then follow 3 commits, one for each child event and then an extra one with all 3 child events.

I believe that in the commit phase of the child UoW I can check if there is a parent and not call the commit method (that actualy appends the events). That should take care of the 3 commits where each child is sent individually, and hopefully the odd commit with the 3 children…

I had some time to come back to this and debug the code. I fount out that it's the code below from method eventsQueue in AbstractEventBus that generates the duplicate commits.

Bruno,

just to make sure we’re not chasing ghosts here. You’re talking about “duplicate commits”, but they’re nothing strange. Each Unit of Work is committed independently (although their progress is synchronized when they are nested).

What is the exact behavior of the system you observe that is not as expected?

Cheers,

Allard