Event bus implementation using EF Core and CAP libraries

Emre Teoman
Borda Technology
3 min readSep 30, 2021

--

One of the biggest challenges of microservice architectures is to ensure data consistency. Some data is transferred from one service to another via asynchronous events to ensure consistency. The transactional outbox pattern is a solution used for event-based communication, which is not difficult to implement. In this pattern, events are written to a table named outbox. The critical point is that the same transaction must complete database operation and write events to the outbox table. Then, the events written to the outbox table are published to the event bus by a worker.

CAP is one of the libraries that has successfully implemented the transactional outbox pattern for .NET Core. Here is the article in which is introduced the CAP library here. This article will talk about how we integrate the transactional outbox pattern into our unit of work implementation using the CAP. First, let us take a look at the UnitOfWork.

Entity Framework (EF) Core is frequently used in ASP.NET Core projects as ORM. Although the repository pattern is one of the commonly used patterns, discussions continue in the community about whether to use the repository pattern with EF Core or not. As explained in “Combining repository pattern and unit of work using EntityFramework Core” article, there are many reasons to use the repository pattern. However, according to the same article, only a repository is not sufficient, and unit of work structure is still needed. In this article, a UnitOfWork structure will be developed and implement the transactional outbox skating into it.

Let’s remember the unit of work structure described in the article mentioned above.

There are three functions in the UnitOfWork class here. These are the CompleteAsync method responsible for the SaveChangesAsync operation and the methods that initiate and commit the transaction. In this article, we will add the AddEvent method in addition to these methods. With this method, we will be able to add events to be sent to the event bus. The unit of work structure will be updated to send event and database operations in the same transaction.

According to the CAP, business logic and event sending can be handled in the same transaction simply as follows.

Let us combine the use of CAP with our unit of work structure. One of the changes will be in the BeginTransaction method. Instead of the BeginTransaction method provided by EFCore, we will use the BeginTransaction extension method in the CAP library. The difference of the method is that it begins a transaction on the DbContext by injecting the ICapPublisher service.

The most significant change will be in the CompleteAsync method. If there is no transaction where the UnitOfWork is used, and there are events to be published, the complete method must open a transaction. So, the CompleteAsync should be updated according to the flow below.

Flowchart of CompleteAsync method.

Complete UnitOfWork structure

The UnitOfWork implementation in the code below can be seen, which can send events with the transactional outbox pattern.

Registering UnitOfWork

UnitOfWork is also registered as scoped service like DbContext.

Defining an event

EventKey attribute is used to define the topic of the event. All subscribers have to subscribe with this key for the event. EventBase is the base class for all events in the architecture because the input parameter type of the AddEvent method will be EventBase. Let’s define an event:

Publishing an event

Below is an example of an API controller that sends an event when a person is created. ApplicationContext and IUnitOfWork services are injected. After adding a person via DbContext, an event is added via unit of work before calling SaveChanges. Then CompleteAsync method of UnitOfWork is called. Adding the person to the person table and writing the event to the outbox table happens in the same local transaction.

Project repository and a sample project that implements the library are available at:

https://github.com/bordatech/unit-of-work-with-outbox

Thanks for reading.

--

--