Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How manage transaction between domain logic and events in DDD?

I am studying on the programming in DDD and event source.

I saw one example that when a domain logic was called (e.g. Order.placeOrder()) it would publish an event (e.g. OrderPlaced). And the event would be sent to MQ as the event store.

The domain logic (Order.placeOrder()) should be an atomic API, and it should have @Transactional annotation if using Spring for the transaction manager.

And now my question is:

  1. How to make sure the DB change and event sending are within the same transaction? i.e. If there any error when committing data into DB, the event should never send to MQ.

    I know that there is solution like XA or 2 phase commit to force the DB update and sending MQ messages within the same transaction. But seems it is not widely used nowadays.

  2. If still using Spring @Transactional annotation and no XA, is it possible that we do some logic after the transaction is committed successfully? What is the best practice to do that?

like image 824
Randy Huang Avatar asked Oct 14 '15 06:10

Randy Huang


1 Answers

The following two properties must hold to have a reliable system:

  • P1: Published domain events MUST describe a change that really happened (i.e. make sure no ghost events start flying around).
  • P2: Changes to the DB that trigger domain events MUST result in an event being published (i.e. don't lose events).

There are the following possibilities to achieve this, all of which I've either used myself or seen being used in a project:

  1. Use a messaging infrastructure that uses the same database as your application, so that a single transaction can be used. This solution is viable when a very simple messaging infrastructure suffices, and the team decides to build it themselves.

  2. Use 2 phase commits. I don't have the impression that this is not used anymore, but maybe it's less talked about, because it isn't fancy technology...

  3. Use some clever trickery to ensure both conditions hold. E.g. with what I call the chicken and egg solution:

    • Always publish events synchronously first, then persist to the DB. This ensures P2 holds.
    • Then use an event processor that inspects the event stream and checks whether an event can be found in the DB. If not, remove the event from the stream. This ensures P1 holds.

Solution 3 requires careful design and review of the guarantees each part of the system makes in terms of failure behavior, so it is probably the most difficult one to get right. But it is also a very elegant solution, once it works.

By the way, I don't agree that Spring annotations should be added to domain objects, but rather to the respective app services. This only as a side note.

like image 77
theDmi Avatar answered Sep 21 '22 00:09

theDmi