π‘ The primary objective of this project is to establish a framework that can facilitate the deployment and operation of a straightforward ECommerce application using cutting-edge technologies and architecture such as Vertical Slice Architecture, CQRS, and DDD in .Net. π
- The Goals of This Project
- Technologies - Libraries
- Structure of Project
- Development Setup
- How to Run
- βοΈ Implementing
Vertical Slice Architectureat the architecture level to create ascalableandmaintainablestructure for the application. - βοΈ Using
Domain Driven Design (DDD)for implementingbusiness processesandvalidation rules. - βοΈ Adopting
CQRSimplementation with theMediatRlibrary for better separation ofwriteandreadoperations. - βοΈ Implementing
MediatRtoreduce couplingand provide support for managingcross-cutting concernswithinpipelines, includingvalidationandtransaction handlingfor the application. - βοΈ Using
Postgresas ourrelational databasemanagement system at the database level. - βοΈ Incorporating
Unit Testing,Integration Testing, andEnd To End Testingfor testing level to ensure therobustnessandreliabilityof the application. - βοΈ Utilizing
Fluent Validationand aValidation Pipeline Behaviouron top ofMediatRto validate requests and responses and ensuredata integrity. - βοΈ Using
Minimal APIfor all endpoints to create alightweightandstreamlinedAPI. - βοΈ Using
AspNetCore OpenApiforgeneratingbuilt-in supportOpenAPI documentationin ASP.NET Core. - βοΈ Using
Docker-Composefor ourdeploymentmechanism to enable easy deployment and scaling of the application.
- βοΈ
.NET 9- .NET Framework and .NET Core, including ASP.NET and ASP.NET Core. - βοΈ
MVC Versioning API- Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core. - βοΈ
EF Core- Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations. - βοΈ
AspNetCore OpenApi- Provides built-in support for OpenAPI document generation in ASP.NET Core. - βοΈ
MediatR- Simple, unambitious mediator implementation in .NET. - βοΈ
FluentValidation- Popular .NET validation library for building strongly-typed validation rules. - βοΈ
Swagger & Swagger UI- Swagger tools for documenting API's built on ASP.NET Core. - βοΈ
Serilog- Simple .NET logging with fully-structured events. - βοΈ
Scrutor- Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection. - βοΈ
AutoMapper- Convention-based object-object mapper in .NET. - βοΈ
NewId- NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs. - βοΈ
Sieve- Sieve is a framework for .NET Core that adds sorting, filtering, and pagination functionality out of the box. - βοΈ
xUnit.net- A free, open source, community-focused unit testing tool for the .NET Framework. - βοΈ
Respawn- Respawn is a small utility to help in resetting test databases to a clean state. - βοΈ
Testcontainers- Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers. - βοΈ
Bogus- Bogus is a simple fake data generator for .NET.
In this project I used vertical slice architecture and feature folder structure to structure my files.
To reduce coupling in our code, we leverage Mediatr and build pipelines on top of it to handle validation, logging, and transactions. Our domain follows Domain-Driven Design principles and employs value objects for business logic. We also incorporate validation into our business processes. When we complete work within our domain, it raises a domain event. Depending on the requirements, we can then react to this event and take appropriate action to further our business goals.
I treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back with vertical slice architecture.
In traditional approach like clean architecture, When adding or changing a feature in an application in n-tire architecture, we are typically touching many layers in an application. We are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer in traditional architecture, we couple vertically along a slice. We minimize coupling between slices, and maximize coupling in a slice.
With this approach, each of our vertical slices can decide for itself how to best fulfill the request. New features only add code, we're not changing shared code and worrying about side effects.
In traditional ASP.net controllers, related action methods are usually grouped in one controller. However, in my recent project, I opted to use the REPR pattern (Route-Endpoint-Presenter-Resource) design pattern instead. With this pattern, each action is given its own small endpoint, consisting of a route, the action, and an IMediator instance (see MediatR), which is handled by a request-specific IRequestHandler to perform business logic before returning the result. This approach not only separates the action logic into individual handlers, but it also supports the Single Responsibility, Open Close Principle and Don't Repeat Yourself principles, resulting in clean and thin controllers.
To achieve better separation of concerns and cross-cutting concerns, I used the Mediator pattern in combination with CQRS (Command Query Responsibility Segregation) to decompose features into small, vertical slices. Each slice has a group of classes specific to that feature, including command, handlers, infrastructure, repository, and controllers. By grouping them together, we can easily maximize performance, scalability, and simplicity, as well as maintain and add features without creating breaking changes or side effects.
With CQRS, we can reduce coupling between layers and tune down specific methods to not follow general conventions. This is achieved by cutting each business functionality into vertical slices, where each command/query handler is a separate slice. As a result, each handler can be a separate code unit, even copy/pasted, allowing us to customize individual methods as needed. In contrast, in a traditional layered architecture, changing the core generic mechanism in one layer can impact all methods, which can be time-consuming and challenging to maintain.
Overall, by using the REPR pattern and CQRS with the Mediator pattern, we can create a better-structured and more maintainable application, with improved separation of concerns.
For installing our requirement package with .NET cli tools, we need to install dotnet tool manifest.
dotnet new tool-manifestAnd after that we can restore our dotnet tools packages with .NET cli tools from .config folder and dotnet-tools.json file.
dotnet tool restore
Run this app in docker using the docker-compose.yml file with the below command at the root of the application:
docker-compose -f ./deployments/docker-compose/docker-compose.yml up -dEach microservice provides API documentation and navigate to /swagger for Swagger OpenAPI or /scalar/v1 for Scalar OpenAPI to visit list of endpoints.
If you like my work, feel free to:
- β this repository. And we will be happy together :)
Thanks a bunch for supporting me!
Thanks to all contributors, you're awesome and this wouldn't be possible without you! The goal is to build a categorized, community-driven collection of very well-known resources.
Please follow this contribution guideline to submit a pull request or create the issue.
This project is made available under the MIT license. See LICENSE for details.