Merging Aggregates Together

Hi Everyone,

I have two aggregate roots (instances of the same Item class), say Item A and Item B, that were mistakenly created twice by the users. The same item was created twice and now the user would like to merge these two items into one. In other works, all non-conflicting information needs to be copied from Item B to Item A and Item B discarded. Furthermore, other aggregate roots may be referencing to the discarded Item B, which needs to be updated too.

What’s a good strategy to achieve this using DDD and AXON Framework?

Thanks in advance,
Albert Attard

Hello Albert,
I would suggest you take a look at Axon-Bank, specifically the BankTransferManagementSaga. In short, the process for merging 2 items is something that your business people need to define. From a technical standpoint, I imagine this is going to involve another aggregate (eg. ItemMerger) and a saga (eg. ItemMergeSaga), but this is pure speculation. Below, I have included my own text-based mapping of how the bank transfer works. If you pop the text into, say, Notepad++ and then double-click the elements, the “flow” will become visibly obvious. This might give you some ideas of how you can implement your solution. Hope this helps.

BankAccount
CreateBankAccountCommand -> BankAccountCreatedEvent
DepositMoneyCommand -> MoneyDepositedEvent
WithdrawMoneyCommand -> MoneyWithdrawnEvent
ReturnMoneyOfFailedBankTransferCommand -> MoneyOfFailedBankTransferReturnedEvent

BankAccountCommandHandler
DebitSourceBankAccountCommand
-> SourceBankAccountDebitedEvent
-> SourceBankAccountDebitRejectedEvent
-> SourceBankAccountNotFoundEvent
CreditDestinationBankAccountCommand
-> DestinationBankAccountCreditedEvent
-> DestinationBankAccountNotFoundEvent

BankTransfer
CreateBankTransferCommand -> BankTransferCreatedEvent
MarkBankTransferCompletedCommand -> BankTransferCompletedEvent
MarkBankTransferFailedCommand -> BankTransferFailedEvent

BankTransferManagementSaga
BankTransferCreatedEvent -> DebitSourceBankAccountCommand
SourceBankAccountNotFoundEvent -> MarkBankTransferFailedCommand
SourceBankAccountDebitRejectedEvent -> MarkBankTransferFailedCommand
SourceBankAccountDebitedEvent -> CreditDestinationBankAccountCommand
DestinationBankAccountNotFoundEvent
-> ReturnMoneyOfFailedBankTransferCommand
-> MarkBankTransferFailedCommand
DestinationBankAccountCreditedEvent -> MarkBankTransferCompletedCommand

Hi Brian,

Thank you very much for your prompt and details reply, Brian. Will try these out an revert.

Can you please explain why would you create an aggregate? What are the benefits that an aggregate will provide? Here I am not challenging your suggestion, but merely trying to understand the reason behind it.

Regards
Albert Attard

Hello Albert,
Sure. Basically, I felt that the merging of 2 aggregates was similar in nature to the BankTransfer which would provide some reference material. You might be able to achieve it without the 2nd aggregate, but it will probably look very kludgy. By introducing the 2nd aggregate, you can keep track of the process (started, failed, completed) and the saga will control the flow by issuing commands to each aggregate involved in the process. If the Item were to have any sort of child entities (eg. variants, warehouse locations, etc…) then the saga might issue commands to add them to the target and then remove them from the source. In your case, this might involve issuing one or more commands for other aggregates to inform them of the merge so that they can be re-pointed appropriately. If there’s a problem along the way, then the saga might issue compensating commands to effectively rollback the process.

Thank you, Brian, for the followup.

I am building a prototype at the moment following your suggestions. I am trying to stay away from Saga for the time being (as an experiment) and I am transferring the required state as part of the events. Is this something that you discourage (transfer state in the event)?

For example, an item (say bicycle) can have many categories (such as toys and outdoors) and the same category (say outdoors) can be used by may items (such as bicycle and barbeque). I have these two types as two aggregates, the Item and the Category aggregates. Each item aggregate has a set of categories ids while each category aggregate has a set of item ids. These are maintained through proper commands and events (such as AddItemToCategoryCommand or AddCategoryToItemCommand and their respective events).

When I remove a category (using RemoveCategoryCommand), an event with the category id together with the items it currently has is fired (CategoryRemovedEvent). Then an event handler (not a saga but a simple service class using the @EventHandler) captures this event and sends commands to each item in this category (such as RemoveCategoryFromItemCommand). The purpose of this class is to create item targetted commands from the events emitted by the category.

I was going to apply a similar logic to the merging. Merging is nothing more than ‘copy and remove’ as a single operation. For simplicity, conflicting changes are ignored. When the aggregate receives a merge command (MergeItemCommand), it creates an event (ItemMergedEvent) which will contain all item’s state including the list of categories it belongs to. An event handler will capture this and creates the proper item and category commands.

Sorry for the long message. Any feedback is appreciated even if this criticises the approach. Please note that versioning is not considered and thus the copy process will never fail, as long as the aggregate with the id exists, otherwise I have to use a Saga and provide compensating commands/events.

Thanks once more
Albert Attard

Hello Albert,
I’m still learning, so I couldn’t really say with absolute certainty. But, what I will say is that events describe something that happened. Using the BankTransferManagementSaga as an example, BankTransferCreatedEvent starts the saga and then a DebitSourceBankAccountCommand is issued as the first step in the process. So, now let’s say that DebitSourceBankAccountCommand is issued for some other reason, you probably don’t want the same “flow” to execute. Not sure if this applies or not, but I wanted to at least provide something concrete. Other than that, I think it might be best if I let someone with more experience chime in.

I will finish the prototype and compare the approach without Saga with that using a Saga and revert.

Thanks a lot for your input Brian

Hello Albert,
No problem. There’s still quite a bit I don’t know, but I felt like I could provide a decent answer here. If you don’t mind, I’d like to see the code once you’re finished for my own learning purposes. Of course, feel free to anonymize package names, etc… and remove any sort of proprietary code.

That’s the least of things. I will attach the project to this thread once ready and we can discuss this further

Hi Albert, Brian,

the concept of “merging” is an interesting one, modelling wise. I assume that with merging, in this case, you mean that you would like to apply all changes that happened to a certain aggregate to another. For example when you discover two user accounts represent the same user, and want to merge details.

One way to see it, is that the “merge” is a single event on the target, where you specify the details that you wish to merge. This may end up to be a large event.

Another way, is to consider a merge on the aggregate that has the state to merge into another. Then, based on this event, another component is brought “up to date” using several commands, based on the state or history of the original. A Saga may help in this case. This Saga could be triggered by the merge event, do some queries to gather the current state of the original, and send commands to the target based on this state.

A third option is to append events to the target’s stream based on the events of the source instance. While this may sound the easiest to do, it doesn’t allow for an aggregate to validate its own state transitions.

The merging part is easy, it’s the finding out which method of doing so that matches the domain the best, is the hard part :wink:

Hope this helps.
Cheers,

Allard

Thank you, Allard, for your reply.

Is it considered bad practice to pass the aggregate’s state as part of the event state?

I had this plan. Say I want to merge Item A with Item B and keep Item A.

  1. Item B receives the command to merge with Item A (@CommandHandler).
  2. Item B creates an event can pass to it all Item B’s state to the event (AggregateLifecycle.appy())
  3. Item B changes its state (@EventSourcingHandler) to merged and the aggregate will reject any further changes
  4. An event listener (not a Saga) listening to the merge event (@EventHandler), creates a set of commands to update the state of Item A only if these are not conflicting. Conflicting changes are simply ignored. An item may belong to a set of categories that needs to be updated too. This is carried out by the same event listener.
    My motivation here is to avoid projections or query as I want to rely on the state of the aggregate and not of some projection.

Any feedback is appreciated.

Hi Albert,

exposing all of the Aggregate state is an anti-pattern. Essentially, an aggregate normally only contains state required to validate commands. Now, it becomes some sort of view model.

A slightly different solution would be for the event handler to retrieve all of Item B’s events, and send commands to A to update it accordingly. This way, the aggregates themselves don’t have to worry about keeping too much state.

Cheers,

Allard

Thanks a lot for your valuable feedback Allard.

I will try to apply your suggestion.

Albert

Hi,

What is the best way to read all events for a given aggregate identifier?

I am trying to implement the following recommendation made by Allard in a previous post.

A slightly different solution would be for the event handler to retrieve all of Item B’s events, and send commands to A to update it accordingly. This way, the aggregates themselves don’t have to worry about keeping too much state.

Thanks in advance
Albert

Hi Albert,

Axon’s EventStore interface has a method readEvents(String aggregateIdentifier) for that.

Cheers,

Note that that method may return a snapshot as first event. If you do not want that, use the method that takes an additional long instead, where that long parameter should be 0, to retrieve all events.

http://www.axonframework.org/apidocs/3.1/org/axonframework/eventsourcing/eventstore/EventStore.html#readEvents-java.lang.String-long-

Cheers,

Allard

Hi Everyone,

Yesterday I thought I replied to this thread, but it seems that I did not. Thanks a lot for your feedback so far.

My goal is to merge Domain Object A with Domain Object B and keep Domain Object A and delete Domain Object B. I tried the following approach (based on Allard’s feedback):

  1. Read all events that were sent to Domain Object B
  2. Converted each event into commands targetted to Domain Object A
  3. Executed these commands (using the command gateway)

This approach worked better than anticipated as all event handlers and sagas were triggered during the process. Unfortunately, I am not convinced yet and would like to get your views as I believe that this is can be done in a better way. What I have learnt so far about the Axon Framework is that if you are writing boilerplate code, then you are doing it wrong.

As mentioned above, I had to convert the events targetted to Domain Object B into commands targetted to Domain Object A. My first (ugly) approach was to read all events for Domain Object B and convert them to commands targetted to Domain Object A using a ladder of if/else statements as shown next.

final Object payload = message.getPayload();

if (payload instanceof AbcEvent) {
/* Convert event to commands and send them */
}

if (payload instanceof DefEvent) {
/* Convert event to commands and send them */
}

/* Do this for all events of interest */

The approach shown above has two issues:

  1. This can get long quickly as we need a new condition for every event
  2. We are replaying all events even the old ones which have been replaced by other new events

Another approach is to use polymorphism where the events themselves will implement an interface, such as

public interface ToCommandsConvertable {
List toCommands(T targetIdentifier);
}

and then each event will be responsible for converting itself into a command targetted to another aggregate by implementing the above method.

I prefer the second option (use polymorphism) as it improves the code cohesion and mitigates the issue of forgetting an event out. But this only addresses the first problem.

The second problem can be addressed by using a new (stateful) class, similar to the responsible aggregate, that plays all events, digests them, and then triggers the minimum number of commands. In other words, this class state is modified while playing the Domain Object B events, and then once it plays all events of interest, it triggers the minimum number of commands targetted to Domain Object A.

As already mentioned, this seems to involve lots of boilerplate code and I want to double check before adopting this approach.

Any feedback is more than welcome.

Hi Albert,

saying “if you are writing boilerplate, then you are doing it wrong” is generally correct. But of course, in Axon we don’t anticipate on all the possible implementations. Only java.lang.Object does :wink:

Using Events is one way, an alternative would be to use queries to fetch the current state. That would remove the need to “remove” intermediate events when certain actions have been overruled or rolled back by another.

Cheers,

Allard