![](https://private-user-images.githubusercontent.com/3188163/289287829-6dc0c717-1b7e-46f8-a34f-12f3d41c30aa.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjE4MTk3OTAsIm5iZiI6MTcyMTgxOTQ5MCwicGF0aCI6Ii8zMTg4MTYzLzI4OTI4NzgyOS02ZGMwYzcxNy0xYjdlLTQ2ZjgtYTM0Zi0xMmYzZDQxYzMwYWEuanBnP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcyNCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MjRUMTExMTMwWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9Nzg5OWM3YjU2MTY3MTkwMGQyZjc1YmY1NjljMGIzMjFkMDY2OTcxNzE3NTNhOTRhOTFlODY5OGZkMmRmMWEyZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.x0nZLQ6-ZVazA7mo2D9k2GP3dIrzii2vD0ShrUCyeI0)
Gym Clean Architecture is a sample project that demonstrates how to implement the Clean Architecture in a .NET application.
- .NET 8
- EF Core 8
To install the project, follow these steps:
git clone git@github.com:alexeipancratov/gym-clean-architecture.git
cd gym-clean-architecture
dotnet restore
In order to run the project, follow these steps:
dotnet run --project src/GymManagement.Api
The project is structured according to the Clean Architecture principles as follows:
src/GymManagement.Api
- presentation layer, which contains the API controllers and is responsible for converting the requests to the application layer and the responses to the clientsrc/GymManagement.Application
- application layer, which contains the application logic and is responsible for coordinating the application, defining the interfaces of the application services, and orchestrating the domain layer using the MediatR librarysrc/GymManagement.Domain
- domain layer, which contains the domain logic and is responsible for defining the domain entities and the core business rulessrc/GymManagement.Infrastructure
- infrastructure layer, which contains the infrastructure logic and is responsible for implementing the interfaces defined in the application layer, such as the repositories and the unit of work, using the Entity Framework Core library
Validation comes in two flavors: model validation and domain validation. Generally, model validation is implemented in the presentation layer or right at the top of the application layer, while domain validation is implemented strictly in the domain layer.
Model validation is responsible for validating the input data, e.g., if username is between 1 and 50 characters long (as required by the database). Domain validation is responsible for validating the business rules, e.g., if the username is unique. Thefefore, acting as an additional layer of validation.
In this project model validation is implemented right at the top of the application layer using the FluentValidation library as a cross-cutting concern using the MediatR's pipeline behaviors (see src/GymManagement.Application/Behaviors/ValidationBehavior.cs
). Validators are implemented at the command request level.
Execute the following commands from the root directory of the project:
dotnet ef migrations add GymsAndSubscriptionGyms -p src/GymManagement.Infrastructure -s src/GymManagement.Api
-
creates a new migration
dotnet ef database update -p src/GymManagement.Infrastructure -s src/GymManagement.Api
- updates the database
This project relies on eventual consistency when handling complicated flows which involve updating data in multiple
places, e.g, DeleteSubscriptionCommandHandler
. In this case, Admin's subscription gets assigned to null, and then
a domain event is being raised - SubscriptionDeletedEvent
. Then corresponding event handlers handle deletion of
actual and related data.
In order to optimize the duration of request execution event handlers will be executed after the response is sent.
This is handled in the EventualConsistencyMiddleware
. This middleware executes its logic inside a DB transaction
so that either all event handlers succeed or none of them do.
This approach is the opposite to "transactional consistency" where an "orchestrator" handles all the logic surrounding a business use-case, thus making request processing faster. There're several benefits to this approach:
- high performance
- flexible error handling (side effects can be retried multiple times in the background without user knowing about it)
- scalability
For authentication we're using Authorized
attribute with the JWT bearer authentication scheme on the controller level.
This attribute can be used for both authentication and authorization, but we're leveraging it only for authorization.
All the authentication logic is handled by the framework, and it's configured in the Infrastructure layer DI registration.
For authorization we're using MediatR behaviors, specifically AuthorizationBehavior
which checks if the user is
authorized to execute the command.
Currently it's being discussed whether it's a good idea to use MediatR for authorization, or if this should be handled in the Presentation layer. MediatR is a good candidate for this, because authorization is a business rule. So it makes sense to handle only authentication in the Presentation layer.
None