Skip to content

Commit f93dae7

Browse files
committed
docs: update README for clarity and improved descriptions
1 parent 78145f4 commit f93dae7

1 file changed

Lines changed: 113 additions & 46 deletions

File tree

README.md

Lines changed: 113 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# ASP.NET Core C# — Clean Architecture, CQRS, Event Sourcing
1+
# ASP.NET Core C# — Clean Architecture, CQRS, and Event Sourcing
22

33
[![Build](https://github.com/jeangatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing/actions/workflows/dotnet.yml/badge.svg)](https://github.com/jeangatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing/actions/workflows/dotnet.yml)
44
[![SonarCloud](https://github.com/JeanGatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing/actions/workflows/sonar-cloud.yml/badge.svg)](https://github.com/JeanGatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing/actions/workflows/sonar-cloud.yml)
@@ -24,61 +24,62 @@
2424
</picture>
2525
</a>
2626

27-
About the repository:
28-
Open source project written in the latest version of ASP.NET Core, implementing the concepts of S.O.L.I.D, Clean Code,
29-
CQRS (Command Query Responsibility Segregation)
27+
This repository is an open-source reference implementation of ASP.NET Core using Clean Architecture, CQRS, and Event Sourcing principles. It is designed to demonstrate how to structure a production-ready solution around separation of concerns, testability, and maintainability.
3028

3129
## Give it a star! ⭐
3230

33-
If you liked this project, learned something, give it a star. Thank you!
31+
If you found this project useful, please consider giving it a star. Thank you!
3432

35-
## **Technologies**
33+
## Technologies
3634

3735
- ASP.NET Core 10
3836
- Entity Framework Core 10
39-
- **EF Compiled Queries** (https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/compiled-queries-linq-to-entities)
40-
- Unit & Integration Tests + xUnit + FluentAssertions
41-
- xUnit (https://github.com/xunit/xunit)
42-
- FluentAssertions (https://github.com/fluentassertions/fluentassertions)
43-
- Polly (https://github.com/App-vNext/Polly)
44-
- AutoMapper (https://github.com/LuckyPennySoftware/AutoMapper)
45-
- FluentValidator (https://github.com/FluentValidation/FluentValidation)
46-
- MediatR (https://github.com/LuckyPennySoftware/MediatR)
47-
- Result (https://github.com/ardalis/Result)
48-
- ~~Swagger UI~~
49-
- OpenApi
50-
- **Scalar** - Interactive API Reference from OpenAPI/Swagger (https://github.com/scalar/scalar)
51-
- HealthChecks
52-
- Microsoft SQL Server
53-
- MongoDB
54-
- Redis (Cache)
55-
- Docker & Docker Compose
56-
- Distroless .NET Images (https://github.com/dotnet/dotnet-docker/blob/main/documentation/distroless.md)
57-
58-
## **Architecture**
37+
- EF Compiled Queries
38+
- xUnit, FluentAssertions, and NSubstitute for automated testing
39+
- Polly for resilience policies
40+
- AutoMapper for object mapping
41+
- FluentValidation for input validation
42+
- MediatR for in-process messaging
43+
- Ardalis.Result for result-based error handling
44+
- OpenAPI and Scalar for API documentation
45+
- Health Checks
46+
- Microsoft SQL Server, MongoDB, and Redis
47+
- Docker and Docker Compose
48+
- Distroless .NET images
49+
50+
## Architecture
5951

6052
![CQRS Pattern](img/cqrs-pattern.png "CQRS Pattern")
6153

62-
- Full architecture with responsibility separation concerns, SOLID and Clean Code
63-
- Domain Driven Design (Layers and Domain Model Pattern)
64-
- Domain Events
65-
- Domain Notification
66-
- Domain Validations
67-
- CQRS
68-
- Event Sourcing
69-
- Unit of Work
70-
- Repository Pattern
71-
- Result Pattern
54+
The solution is organized around a layered, domain-first design that emphasizes:
55+
56+
- Clear separation of responsibilities across application, domain, infrastructure, and presentation layers
57+
- SOLID principles and clean code practices
58+
- Domain-driven design with entities, value objects, and aggregates
59+
- Domain events and notifications
60+
- CQRS for separating command and query responsibilities
61+
- Event Sourcing for persisting state transitions
62+
- Repository and Unit of Work patterns
63+
- Result-based handling for predictable application outcomes
64+
65+
## Prerequisites
66+
67+
Before running the application locally, make sure you have:
68+
69+
- .NET SDK 10
70+
- Docker Desktop (or Docker Engine with Compose support)
71+
- A terminal with access to the repository root
7272

7373
## Running the application
7474

75-
After cloning the repository to the desired folder, run the command in the terminal at the root of the project:
75+
1. Clone the repository and navigate to the project root.
76+
2. Restore and build the solution:
7677

77-
```csharp
78+
```bash
7879
dotnet clean Shop.slnx --nologo /tl && dotnet build Shop.slnx --nologo /tl
7980
```
8081

81-
Set passwords in the `.env` file:
82+
3. Create a `.env` file with the required environment variables:
8283

8384
```yaml
8485
MSSQL_SA_PASSWORD=YOUR_STRONG_!Passw0rd
@@ -89,23 +90,89 @@ REDIS_PORT=6379
8990
ASPNETCORE_ENVIRONMENT=Development
9091
```
9192

92-
Next step, run the command in the terminal:
93+
4. Start the infrastructure and application containers:
9394

94-
```csharp
95-
docker-compose up --build
95+
```bash
96+
docker compose up --build
9697
```
9798

98-
Now just open the url in the browser:
99+
5. Open the API documentation in your browser:
99100

100-
```csharp
101+
```text
101102
http://localhost:{port}/scalar/v1
102103
```
103104

104-
## MiniProfiler for .NET
105+
## Unit Testing
105106

106-
To access the page with the performance indicators and performance:
107+
This repository uses a layered testing strategy to keep the solution reliable while preserving the principles of Clean Architecture and CQRS.
108+
109+
- Unit tests are located in [tests/Shop.UnitTests](tests/Shop.UnitTests) and focus on domain entities, value objects, validators, handlers, queries, and mapping logic.
110+
- Integration tests are located in [tests/Shop.IntegrationTests](tests/Shop.IntegrationTests) and cover API and infrastructure behavior.
111+
112+
### Test stack
113+
114+
The unit test project in [tests/Shop.UnitTests/Shop.UnitTests.csproj](tests/Shop.UnitTests/Shop.UnitTests.csproj) uses:
115+
116+
- xUnit for test execution
117+
- FluentAssertions for readable assertions
118+
- NSubstitute for dependency mocking
119+
- Coverlet for coverage reporting
120+
- Microsoft.EntityFrameworkCore.Sqlite for database-backed tests
121+
122+
### How to run tests
123+
124+
From the repository root, run:
125+
126+
```bash
127+
dotnet test tests/Shop.UnitTests/Shop.UnitTests.csproj
128+
```
129+
130+
To run the full solution test suite:
131+
132+
```bash
133+
dotnet test Shop.slnx
134+
```
135+
136+
To run a subset of tests by name:
137+
138+
```bash
139+
dotnet test tests/Shop.UnitTests/Shop.UnitTests.csproj --filter "FullyQualifiedName~CreateCustomer"
140+
```
141+
142+
### Example test case
143+
144+
A typical unit test follows the xUnit and FluentAssertions pattern used throughout the project:
107145

108146
```csharp
147+
public class CreateCustomerCommandValidatorTests
148+
{
149+
[Fact]
150+
public void Validate_Should_Fail_When_EmailIsInvalid()
151+
{
152+
var validator = new CreateCustomerCommandValidator();
153+
var command = new CreateCustomerCommand("Jane Doe", "invalid-email");
154+
155+
var result = validator.Validate(command);
156+
157+
result.IsValid.Should().BeFalse();
158+
result.Errors.Should().ContainSingle(e => e.PropertyName == "Email");
159+
}
160+
}
161+
```
162+
163+
### Best practices for CQRS and Event Sourcing
164+
165+
- Keep unit tests fast and isolated; prefer deterministic inputs and small collaborators over full infrastructure dependencies.
166+
- Test command, query, validator, and domain aggregate behavior directly rather than relying on implementation details.
167+
- For CQRS, verify that handlers and validators produce the expected results and side effects.
168+
- For Event Sourcing, assert that domain events are emitted correctly and that aggregates can be reconstructed from historical events.
169+
- Use descriptive test names so failures clearly communicate what behavior broke.
170+
171+
## MiniProfiler for .NET
172+
173+
To inspect performance traces while the application is running, open:
174+
175+
```text
109176
http://localhost:{port}/profiler/results-index
110177
```
111178

0 commit comments

Comments
 (0)