What’s the Outbox Pattern?
A reliable way of saving state to your database and publishing a message/event to a message broker. Why do you need it? When you get into messaging, you will often find code that needs to save state to your database, and then publish a message/event to a message broker. Unfortunately, because they are two different resources, you need a distributed transaction to make these atomic. There is another option to use the Outbox Pattern which does not require a distributed transaction and most messaging libraries support it
The Transactional outbox pattern uses the primary persistence layer (database, either SQL or NoSQL) as a temporary message queue. The service that sends messages has an OUTBOX database table. As part of the database transaction that creates, updates, and deletes entities, the service sends messages by inserting them into the OUTBOX table. Atomicity is guaranteed because this is a local ACID transaction. In the case of a NoSql database, each aggregate stored as a document in the database has an attribute that is a collection of messages that need to be published. When a service updates an entity in the database, it appends a message to that list. This is atomic because it’s done with a single database operation. A publisher component then reads the OUTBOX table or the outbox collection in the document and publishes the messages to a message broker.
Admin need when a client creates/update tenant or company email send to him to know which company is registered like this
First works synchronous
1-Update/Create Tenant 2-Save Changes 3-Send Mail
the cons for this approach are we don’t guarantee that the after saving changes in the database may be when to sending mail problem happens:
The network is broken or exception happened in the SendMail Service and we lost to send mail to admin that tenant created so we didn’t achieve Atomicity (all or nothing), and one of the requirements is admin must receive mail when anything happens in tenants so we have not achieved this req.
1-Update/Create tenant 2- Add event in OutBox Table 3- Save Changes 4-Publisher check for any data added in the outbox table and send it to the broker
in this approach, we are guarantee that two services are Atomicity because they have the same Destination (Database) and at less event is consumed once, if the broker is idle we can fix it and consume messages from the outbox table
Let’s implement our pattern
I implement this example using : 1-.Net 5 and Entity Framework 2- Third-party package Quartz.AspNetCore
Quartz.NET is a full-featured, open-source job scheduling system that can be used from smallest apps to large-scale enterprise systems.
Create OutboxMessage class as a temporary message queue
Added to Dbcontext
Second, we create our job using Quartz
Config this job to work every 10 seconds to check if the event was added in the outbox table or not and send it to the mail
I using a MediatR package
I using Mailtrap to test mail
Let’s test our code, let’s generate organization
Tenant is created
Mail is consume
We can use this pattern in another scenario like 1- when employees register send mail to it 2- when order is created other services need to consume this event
Source: Medium - Amrelsher
The Tech Platform