Today, architectures such as the onion or hexagonal, provide an important help for testabillity and maintenance of code, independence with external frameworks. etc …
In this tutorial, I will show how to use the clean architecture, with methods and tools such as domain driven disign (DDD), Test (Behavior) Driven development (TDD), CQRS, Event Sourcing, Containerization, Oauth2 & Oidc to build a microservices architecture
For more information on clean architecture, I suggest you read this article by Robert C. Martin (Uncle Bob):
Building microservices through Event Driven Architecture part1: application specific business rules
The Dependency Rule
The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.
The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes, functions, classes. variables, or any other named software entity.
By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.
Robert C. Martin (Uncle Bob)
COMMAND QUERY RESPONSABILITY SEGREGATION (CQRS)
CQRS Separate Commands From Queries.
Commands are operations that change the application state and return no data. Queries are operations that return data but do not change application state.
So, CQRS will be a very usefull concept, in the world of microservices by creating our application with two databases:
- a relational datadase, which is optimized for writing on the command side.
- a NoSQL database on the query side so as to read data as fast as possible.
Since most applications read data much more often than they write data, in our approach of containerization, we can scale the command side on 2 pods and query side on 10 pods for example
DOMAIN DRIVEN DESIGN
CQRS fits in domain-driven design . DDD focuses on building rich domain models to tackle complex business logic.
Changing the data causes more bugs. So, having a clear perception of which part of the application change the data and which part of the application does not change the data will help with maintainability and debugging
Event sourcing stores all changes to an object as a series of events in an event store
I will use this concept to build the following :
- Domain service implements domain-related concepts (entity, value object, aggregates, domain events), records a command in a relational database, and an event in the event store. all as a Unit for the purpose of data change.
- a producer takes the event from the event store and sends it to the service bus ( eventstore is a append only table and use ubiquitous language)
- a consumer, subscriber to service bus, takes the event from the service bus and register it as pre-computed data to a nosql database
- ReadModel service query the NOSQL database.
- Everything that happens is saved in the event store
I will build a system that helps speakers and attendees to register and follow events (conferences, talks, meetups, etc…)
My project is structured as follows :
The innermost layer that holds the core domain. It contains our domain objects and business rules. and defines our External Interfaces.
Databases, network connections, filesystem, UI or special frameworks. are not allowed.
The core domain have no knowledge of anything outside of themselve
Those dependencies, and their implementations are injected into our core domain using Interfaces.
This lawyer point to core domain and contain the application specific business rules.
Orchestrate the data flow and use the domain model
Have no dependency to a database, UI or special frameworks
This layer holds the Web, UI and Presenter concerns. In the context of our API, this means it accepts input in the form of http requests over the network (POST/PUT/PATCH/DELETE) and returns its output as content formatted as JSON.
This layer holds the Database and Gateway concerns. In here, we define data access layer, repositories, etc.…
It contains the physical implementation of the interfaces defined in our domain layer
TEST DRIVEN DEVELOPMENT
Implementing the “Speech Registration” use case
To make my tests green, the first thing I need to implement is RegisterSpeechUseCase
The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circl
Robert C. Martin (Uncle Bob)
- So, let us define IRegisterSpeechUseCase interface its implementation RegisterSpeechUseCase. These types belongs to EduSync.Speech.Application
It takes an input object as a command,
Then the interface
Then RegisterSpeechUseCase will look like this:
Let us defines dependencies such as IUnitOfWork and ISpeechRepository, these interfaces belongs to core domain and will be implemented on infrastrucrure.
ISpeechRepository need a Speech Entity, so let create it on core domain
In this stage, evry thing compile succesfully but my tests failed
As you can see, tests fails because I verify that CreateAsync and commit are called, so let us call
SpeechRepository.CreateAsync and IUnitOfWork.Commit on RegisterSpeechUseCase class
then create a mock of SpeechRepository.CreateAsync and IUnitOfWork.Commit on arrange section of my unit test
At this stage all tests are green but my code coverage is not enough :
for example if I comment out this block my tests will succeed but my app will crash at runtime if command is null
Let us add a new test to fix it
Finally code coverage is 100% for LogCorner.EduSync.Speech.Application
But what happend if I permute values ?
var title = command.Type;
var urlValue = command.Title;
var description = command.Url;
var type = command.Description;
All test will succeed but my application will be in a invalid state because it will insert the title instead of the url, ….
I can fix it using moqSpeechRepository.Verify in test assertions but I’ll leave it like that and fix it when implementing my domain with the introduction of the values objects
On next step, I will implement domain model
Source code is available here : RegisterSpeechUseCase