As mentioned in part one, each service is designed with Clean Architecture principles. Clean Architecture is a smart way of structuring an application, and thrives on some of the principles of good Object Oriented Principles such as SOLID and DRY. But also some design principles as Domain-Driven Design.
Domain-Driven Design is first introduced by Eric Evans in his book “Domain-Driven design”. Domain-Driven design places the domain rather than the data such as models in the center of an application. The domain means you need to have a great understanding of what kind of problem the software is going to solve. Imagine making a banking system, without knowing what banking really is? To create a system in this way the final product will often not meet the criteria/requirements of the company itself.
However, Clean Architecture is embracing this idea of putting the domain in the center, and uses a strict codex for how dependencies should be in your program.
Here is the black arrows the dependencies meaning that the must vulnerable layers, the domain/application is in the middle, and the more non-vulnerable layers are the most outer layers. By vulnerable layers I mean the most often to change.
The application structure
The application structure is fairly organized and therefore easy to setup.
Here you can see my solution structure, which gives a clear hint of the dependencies in th program.
The API gateway has no dependencies what so ever to any of the services. The API gateway functions as a kind of proxy redirecting requests to the specific service.
The CrossCuttingConcerns project is a project consisting of different options / setup functions for services. This is so I don’t have to keep repeating the same code over and over again, essentially following the DRY principle (Don’t Repeat Yourself) making my code less prone to bugs.
If we look at RequestService structure we will see clear indicators of Clean Architecture.
As we can see, we have 5 different projects essentially mirroring the naming from Clean Architecture. Let’s try open the projects.
As we see on the picture above, I split up the infrastructure layer into two separated projects. One dealing with the more generalized Infrastructure such as NotificationService (which can be used with a messagebroker) and a Persistence project using a Code-first Entity Framework Core approach.
A SharedKernel/SeedWork is essential a concept from DDD. Reusable code which may not be changed without consulting DevOps or a another team working on the same project. This could be moved to CrossCuttingConcerns, however I haven’t got around to do it.
The domain layer
The domain layer consists of our domain models, entities, the data which is saved in the database and mapped to DTO’s to be presented. I tried to implement interfaces, and using the repository pattern. However, the Entity Framework Core already is a repository pattern, and there another abstraction would just leave me more confused and would be unnecessary code. Therefore I went with no repository using Entity Framework Cores DbSets instead.
The application layer / use case layer.
This is where the core business logic is handled. I chose the CQRS pattern essentially splitting my application into two different flows/models (Commands and queries). Commands changes the state of the program - writes to the program. Queries reads from a database, and presents it. So essentially the flow would be like this:
In an ideal world, the Query would read from a cache to make small less stress on the database. Commands will then report to the cache to change state, whenever a command is successfully executed.
The validation is made with use of the library FluentValidation which gives the possibility to make use of ValidationFailure exception. An easy way of making custom validation errors and returning BadRequest status code if a command/query is not validated. The whole handler pattern is made with Mediator pattern by using the Mediatr library.
The smart thing about an easy structure like this, is that it is easy for new developers to figure out how to implement new functionality to the application. However it can easy be over engineering, if the domain is not complex enough. I made this as a proof of concept, since this could easy be made using a normal CRUD model.
The infrastructure layer is fairly simple. I used FluentAPI to configure my entities, rather than Data annotations, since I didn’t want to have that logic in my domain layer (remember to keep dependencies in the domain layer at a minimum to none). The application layer needed to know about the different DbSets to execute handler logic. To do this, I made an interface in the application layer essentially mirroring the DbContext and implemented this in the persistence layer. Because of this, I inverted the dependency, essentially achieving the right dependencies.
In the next part we will see how I made the two services communicate together via. RestEase HTTP Clients.