Building microservices through Event Driven Architecture part8: Implementing EventSourcing on Application

This tutorial is the 8th part of a series : Building microservices through Event Driven Architecture.

The previous step is about Building microservices through Event Driven Architecture part7: Implementing EventSourcing on Repositories : https://logcorner.com/building-microservices-through-event-driven-architecture-part7-implementing-eventsourcing-on-repositories/

During this journey, I will  talk about Event Sourcing implementation on Application.

This layer surrounds the domain and implements the uses cases (the application specific business rules).

It orchestrate the data flow and use the domain model and infrastructures, and have no dependency to a database, UI or special frameworks.

For the purposes of our eventsourcing system , I will take all the uncommited events from the aggregate and call a function of the repository which will have the responsibility to save the events in the eventstore. So I will call the function  AppendAsync(EventStore @event) of IEventStoreRepository.

Because I have not yet implemented the updates, I will not take care about  the aggregate version, (I have a single post method that creates a speech, so the aggregate version  will always be equal to zero).
When I will  implement the updates, I will take care about  the aggregate  version  in the application and presentation layers.

EVENT SOURCING INTERFACES

I can define two interfaces IEventSourcingSubscriber and IEventSourcingHandler, you can use another naming convention, but for now I leave them as they are.

  • Subscribe  is a function that get all uncommited events from the aggregate and for each event call a function Handle that take a event and  the current aggregate version as inputs.

IEventSourcingHandler

  • Handle is a function that serialize the event into a string and call  AppendAsync(EventStore @event) of IEventStoreRepository .

EVENT SOURCING IMPLEMENTATION

EVENTSOURCING SUBSCRIBER IMPLEMENTATION

So the first test sould be  :  Subscribe with no uncommitted events should not call handle

I have no uncommitted events , I will do nothing. Then the Assert section will look like this :

mockEventSourcingHandler.Verify(m => m.Handle(It.IsAny<Event>(),It.IsAny<long>()), Times.Never, “Handle must not be called”);

TEST CASE 1 : Subscribe with no uncommitted events should not call handle:

SubscribeWithNoUncommittedEventsShouldNotCallHandle

Here, I will finish the implementation of my function

EventSourcingSubscriber

TEST CASE 2 : Subscribe with uncommitted events should call handle only once:

mockEventSourcingHandler.Verify(m => m.Handle(It.IsAny<Event>(),It.IsAny<long>()), Times.Once, “Handle must be called only once”).

In the Assert section, I can verify that the Handle function is called only once.

SubscribeWithUncommittedEventsShouldCallHandleOnlyOnce

And the final implementation of EventSourcingSubscriber should look like this :

I call handle function for each event.

EventSourcingSubscriberFinal

EVENTSOURCING  HANDLER IMPLEMENTATION

TEST CASE 3 : Handle with null events should raise EventNullException:

I mock some dependencies and verify that if the event is null, then handle should raise an exception.

HandleWithNullEventsShouldRaiseEventNullException

Here is the implementation of the test.

EventSourcingHandlerNoEvents

TEST CASE 4 : Handle with events should call AppendAsync :

Here I verify that if the event is not null, then Handle should call AppendAsync

HandleWithEventsShouldCallAppendAsync

Here is the implementation of the test. You can observe that I use a IEventSerializer interface to serialize the event into a json string, this json string will be the payload of the event stream.

EventSourcingHandlerWithEvents

UPDATE REGISTERSPEECHUSECASE

Then upade the RegisterSpeechUseCase and call Subscribe function :    await _domainEventSubscriber.Subscribe(speech);

RegisterSpeechUseCase Handle

UPDATE PRESENTATION

Open Startup.cs file and configure some dependency injections

StartUp

Open appsettings.Development.json and upade the ConnectionStrings to use the appropriate database server, database name  and credentials.

ConnectionStrings

Scripts to create dabase are located in LogCorner.EduSync.Speech.Database project

Project sql

TEST USING POSTAMAN

TEST LOCAL CODE USING VISUAL STUDIO

Select LogCorner.EduSync.Speech.Presentation projet and hit F5

Launch postman and  run the following HTTP Post

Endpoint : http://localhost:62694/api/speech

Method : POST

Content-Type: application/json

Body : {
“Title”:”Le Lorem Ipsum est simplement du faux texte”,
“Description”:”Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l’imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte”,
“Url”:”http://www.yahoo_1.fr”,
“Type”:”3″
}

Postman

Open Sql server Management studio and run the following commands :

SELECT * FROM [LogCorner.EduSync.Speech.Data].[dbo].[Speech]
SELECT * FROM [LogCorner.EduSync.Speech.Data].[dbo].[EventStore]

The result should look like this :

sqlresult

As a result, I should have records on Speech table and on EventStore table, the version is always equal to zero because this tutorial does not cover updates.

TEST LOCAL CODE USING DOCKER

locate \LogCorner.EduSync.Command\src folder (the folder where is located docker-compose.yml file) and run the following command

  • docker-compose build
  • docker-compose up
  • docker ps –all –format “table {{.ID}}\t{{.Image}}\t{{.Names}}”

ListImages

Launch postman and run the following HTTP Post

Endpoint : http://localhost:8080/api/speech

Method : POST

Content-Type: application/json

Body : {
“Title”:”Le Lorem Ipsum est simplement du faux texte”,
“Description”:”Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l’imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte”,
“Url”:”http://www.yahoo_1.fr”,
“Type”:”3″
}

Finally , inspect the running containers like this :

Open a bash shell  by running the following command ( where 0b is first letters of  logcorner.edusync.speech.presentation.data container Id)

  • Docker exec -it 0b “bash”

Connect to sql server linux

  • /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P ‘PassW0rd’

Run sql queries

use [LogCorner.EduSync.Speech.Database]
go

select * from [dbo].[Speech]
go

select * from [dbo].[eventstore]
go

Testdocker

As a result, I should have records on Speech table and on EventStore table, the version is always equal to zero because this tutorial does not cover updates.

Cource code of this article is available here  (Feature/Task/EventSourcingApplication)

https://github.com/logcorner/LogCorner.EduSync.Speech.Command/tree/Feature/Task/EventSourcingApplication

Regards

Gora LEYE

I'm a microsoft most valuable professional (MVP) .NET Architect and Technical Expert skills located in Paris (FRANCE). The purpose of this blog is mainly to post general .NET tips and tricks, www.masterconduite.com Gora LEYE

Support us

BMC logoBuy me a coffee