If you liked my work and want to support me, please give it a star. Thanks!
This repository is a sandbox to experiment with new techniques, concepts, and technologies using Clean Architecture concepts. Here you will find Domain-Driven Design (DDD), Command Query Responsibility Segregation (CQRS), SOLID Design Principles, Design Patterns, and more.
docker-compose -f ./infra/docker-compose.yml -f ./infra/docker-compose.dev.yml -p clean-arch-dotnet-dev up -d
http://localhost:5010/swagger
dotnet test CleanArchDotnet.sln
docker-compose -f ./infra/docker-compose.yml -f ./infra/docker-compose.test.yml -p clean-arch-dotnet-test up -d
I'm using a Clean Architecture model, splitting the solution into Core, Infra, Services, and Shared Kernel layers.
The Core layer is responsible for the Domain Entities, Value Objects, Domain Services, Domain Events, Use Cases and Abstractions for communication with external layers. The main goal of this architecture is to keep the Core, where the business lives, isolated from external dependencies implementation details.
The organization follows "The Screaming Architecture" concept by Uncle Bob that purposes that your application architecture should scream what the system does. So I've preferred an organization by context rather than by component type.
Another principle that I'm following is CQRS/CQS, so the Use Cases are divided into Write or Read operations. Also, any Use Case has its owns Command, Query, Result, and Handler.
The Infra layer is responsible for Repository Concerns (Schemas, Repository Implementations, and Database Connections), Dependency Injection, Application Settings, and External Providers for integration or services consumption.
These are entry points for Core Use Cases consumption. Can be APIs, gRPC Services, Serverless Functions, Workers, and more. As the domain does not depend upon, the consumption can be anything.
These are common resources used between application layers. This project avoids circular dependencies between the layers.
This Architecture follows the Dependency Rule, where all dependencies points toward the architecture center (AKA the Core).
The Infra layer depends upon the Core layer, while the Core uses abstractions with the Dependency Inversion Principle to consume infra resources, like data access.
The Services layer depends upon the Infra due to Dependency Injection and Application Settings needs. The Services layer depends upon the Core too and communicates with the Core through abstractions.
The Shared Kernel layer helps to avoid circular dependencies between projects. All projects depend upon the Shared Kernel.
The application is containerized using Docker and automated using Docker Compose.
The docker-compose file has overridden versions for Dev and Load Tests environments. I took the caution not to use default ports in containers to avoid conflict with other containers/servers that you may have using these ports.
Another point is that the application is working with Entity Framework Core ORM, and the migrations are applied at the Application Startup.
Fast and without external dependencies. These tests are focused on the smaller units of code. The Todo Entity and the DomainValidationProvider are unit tested since they don't have extra dependencies to work.
With dependencies and focused on some internal functionality. In this application context, the Integration Tests are focused on use cases. External dependencies could be mocked or replaced (I'm replacing the Database with an In-memory one).
There are discussions about how many assertions you should have in a test, the boundaries, and soo. Here, following my experience and trying to be very comprehensive in these tests. I'm testing from the input until the persisted data.
These tests focus on test application entry points and returns. To avoid inserting test data into the Database. I'm using an In-memory Database one.
There is a whole separated structure for Load Tests, with containers and tools. The application uses the K6 tool. The K6 idea is to simplify load test where basic JavaScript and HTTP knowledge is sufficient to build load tests.
After executing the Load Tests, you can view results at /tests/CleanArchDotnet.Tests.Load/k6/results
folder. The result.json
shows a detailed report, and the export.json
is a resumed version with a more general view.
Different from other tests, the Load Tests are executed and analyzed separately.
- ASPNET.Core - Web Framework
- EF.Core - Object Relational Mapper (ORM)
- Database - PostgreSQL
- Docker - Container Management
- Docker Compose - Multi-Container Management
- K6 - Load Tests Tool
- Mediatr - Use Cases Decoupling and Behaviours Pipelines
- FluentValidation - Data Validation
- Bogus - Fake data generation for tests
- FluentAssertions - Add fluent pattern to tests assertions
- MicrosoftPackages - Dependency Injection, In-Memory Database, and more