Suppose we have the following:
Aggregates DDD A and B, A may refer to B.
Microservice A management, which provides the following commands:
- create a
- remove A
- link A to B
- disconnect A from B
Microservice B Management, which provides the following commands:
Successfully creating, deleting, linking or disconnecting always leads to the output of the corresponding event using the microservice that performed the action.
What is the best way to create an event-driven architecture for these two microservices so that:
- A and B will always end up matching each other. By consistency, I mean that A should not refer to B if B does not exist.
- Events from both microservices can be easily projected in a separate reading model, on which queries can be executed covering both A and B
In particular, the following examples can lead to short-term inconsistent states, but in all cases the sequence must ultimately be restored:
Example 1
- Initial Consent State: A exists, B does not support, A is not associated with B
- Command: Link A to B
Example 2
- Initial agreed state: exists A, exists B, A is associated with B
- Command: delete B
Example 3
- Initial agreed state: exists A, exists B, A is not associated with B
- Two simultaneous commands: link A to B and delete B
I have two solutions.
Solution 1
- Microservice A only allows you to associate A with B if it previously received the event "B created" and did not delete "B".
- Microservice B only allows you to delete B if it has not previously received the event “A associated with B”, or if this event was followed by the event “A unrelated to B”.
- Microservice A listens for events "B deleted" and, after receiving such an event, disconnects A from B (for a race condition in which B is deleted before it receives A associated with event B).
Solution 2:
- Microservice A always allows you to associate A with B.
- Microservice B listens for events “A connected to B” and, after receiving such an event, checks if B exists. If this is not the case, it throws a “link to B failed” event.
- Microservice A listens for events "B deleted" and "link to B excluded" and, upon receipt of such an event, separates A from B.
EDIT: Solution 3 proposed by Guillaume:
- Microservice A only allows you to bind A to B if the B deleted event was not previously received.
- Microservice B always allows you to delete B.
- Microservice A listens for events "B deleted" and, after receiving such an event, disconnects A from B.
The advantage that I see for solution 2 is that microservices do not need to keep track of past events emitted by another service. In solution 1, basically every microservice should support a readable model of the other.
A potential disadvantage of solution 2 may be the added complexity of designing these events in the reading model, especially if additional microservices and aggregates following the same template are added to the system.
Are there other (dis) advantages for one or the other solution, or even an anti-pattern that I don't know about, should be avoided at all costs? Is there a better solution than two suggestions?
Any advice would be appreciated.
source share