I've read and tried the Transactional Outbox pattern for communicating between services.
It is clear to me what are the benefits of this pattern, as it has two main parts:
- By using a transaction, we promise that if the resource was saved correctly to the DB, so does the event in the outbox table, so we know an event will be sent to the message broker.
- Using a message relay service that listens on the outbox table, guarantees the event will be sent, even if the message broker is down atm.
However, the first advantage seems to break the clean-architecture idea, since it puts the logic and responsibility in the data layer. While, as I see it, the decision to publish an event is BL.
The repo is responsible to know what to publish to the outbox table. It can be difficult to reuse code to publish events, as it needs to be encapsulated into each transaction.
edit: I've read this question: Is "Transactional outbox" an anti-pattern?, but the author asked about SRP, which pretty much hijacked the discussion toward this principle. (IMO it's not directly related to SRP, as this principle is often misused)
What I would like to do?
I would like to create the functionality to publish an event to the message broker which will be used by all the services.
Simply, the BL service will have to do:
repo.SaveResource(resource)
eventProvider.PublishEvent(event)
I could also use a decorator to publish events, which keeps the main BL service agnostic, as sometimes we need to publish events not necessarily when inserting the resource to the DB.
The downside of this is of course I have dual-write that the transactional outbox pattern fixes. But I wonder if it's worth it because I lose clarity and separation of concerns.
I wonder if it's not enough to just revert the operation in case the publishEvent failed
repo.SaveResource(resource)
eventProvider.PublishEvent(event)
// failsrepo.DeleteResource(resource)
Of course, the DeleteResource can also fail, but IMO it's not enough of an argument to use the outbox pattern.
Farther more, the transactional outbox pretty much says that whenever I save a resource in the DB, I want to publish an event as well. It's not always the case, as we have other dependencies and processes we need to do (before or after the insertion to the DB) for the resource to be active. I wouldn't want to create different methods in the repo for insertion with/without an event. We already usually have retry and revert mechanisms which can take place here as well.