Hexagonal Architecture with Dotnet 6 — Part 3: Domain layer
First of all, let’s refresh what this layer has and does, it’s something like:
The Domain Layer contains any core domain logic. It deals entirely with domain concepts and lacks knowledge of the outer layers.
- Hexagonal Architecture Design Pattern. Click here for more info.
The reason we start with the Domain layer is simple. It is an independent layer, all the others depend on some other.
Now, taking this image as a template, we start creating folders and classes.
The code I use in these articles is available on Github. Click here. It says this, let’s go to the code!!
One thing you should know about nuget packages in the domain layer (focus on the <Itemgroup> tag):
We’ll start with the Entities folder:
The AuditableEntity class. It is a base class to inherit from, it contains basic props like Id, CreatedAt, etc.
The Product entity, as its name says, has all the properties about the product. Inherits properties from AuditableEntity.
Now it’s time for DTOs (Data Transfer Objects). Click here for more information on DTO.
We create DTOs for Command (write actions), Query (read actions) and base DTOs to inherit.
Base DTOs to inherit are:
And:
In the following article I will explain why these two BaseDTO.
To use with command (write actions) we’ll create:
To use with queries (read actions) we’ll create:
Maybe, you will think, why do we create two classes for queries, when we see the infrastructure layer you will know why. The same applies to the BaseDTO classes.
It’s time for the Mappers. Here we use Automapper (it’s a Nuget).
If you don’t know about Automapper, I’ll create an article about it. For now, do what I do… LOL… or if you want some documentation you can click here.
Validation helps us that the required props have valid data, for this, we will create a validation for each command dto model. In this case to CreateProductDTO and UpdateProductDTO. To do that, we’ll use FluentValidation.
If you don’t know about FluentValidation, I created an article about it. Read it here.
The extensions folder in the Domain (note: we probably created another extensions folder but not for the Domain, it will be created for Common use).
Here, we will create a QueryableExtensions.
The FilterByPrice method helps us make a product query that we will see in a few minutes.
Another folder is Enums, there we have all the enums that are related to the domain layer, with some entity, methods or any code related to the domain logic.
In this case, we’ll use this enum in a method.
And, the last, but not because it is the last is less important. The Interfaces folder. Here we have all the contracts to implement in the Infrastructure layer (we will see it later).
IGenericRepository has contracts to read and write generic actions like: GetById, Create, Update and more…
IUnitOfWork has contracts to get an instance of ProductRepository and GenericRepository<Product, ProductDTO>, we also have Commit method to save all tracking entities.
IProductRepository has specific methods contracts, and as you can see, we use the PriceFilterOptions enum as a parameter here.
This is all for now, in the next article we continue with the Infrastructure layer.