Skip to content
CAI
Software that uses CAICheck a score

jeangatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing

57.4

Adequate · 20 September 2026

2.8k

lines of production code

C#

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is a customer management service built on .NET 10 using Clean Architecture, CQRS, and Event Sourcing principles. It exposes a public REST API to handle the creation, retrieval, update, and deletion of customer records, enforcing data integrity through domain validation and email uniqueness checks. The architecture separates write operations, which persist to SQL Server via Entity Framework Core, from read operations, which query a MongoDB database through a synchronized read model.

Features

Add Email value object with validation

A new Email value object has been added to the domain layer, providing a structured way to handle email addresses. It includes a Create factory method that validates input using regex patterns and returns a Result type, ensuring that only valid, trimmed, and lowercased email addresses are accepted. This introduces a new capability for enforcing email format constraints within the application's domain model.

src/Shop.Domain/ValueObjects · high confidence

Add command execution logging behavior

A new LoggingBehavior pipeline behavior has been added to the application layer to automatically log the name and execution duration of MediatR commands. This provides visibility into command processing times and helps identify performance bottlenecks without requiring manual instrumentation in individual handlers.

src/Shop.Application/Behaviors · high confidence

Added CreatedCustomerResponse for customer creation results

A new response model, CreatedCustomerResponse, has been introduced in the Customer application layer to represent the outcome of a customer creation operation. This sealed class implements the IResponse interface and exposes the created customer's unique identifier (Guid), providing a structured return type for API consumers or internal handlers.

src/Shop.Application/Customer/Responses · high confidence

Added customer query definitions and validation

New query request classes for retrieving customer data have been introduced in the application layer: GetAllCustomerQuery for fetching a list of customers and GetCustomerByIdQuery for retrieving a specific customer by ID. Additionally, a validator has been added to ensure the ID parameter for the single-customer query is not empty, enforcing input validation before processing.

src/Shop.Query/Application/Customer/Queries · high confidence

Initial project scaffolding with .NET 10, Docker, and Clean Architecture

The repository is initialized with a complete project structure based on Clean Architecture, CQRS, and Event Sourcing principles. The solution targets .NET 10 (SDK 10.0.204) and uses the new .slnx solution format. It includes a Docker Compose setup to run the application alongside Microsoft SQL Server, MongoDB, and Redis, with configuration managed via a .env file. Development tooling is established through an .editorconfig with Roslynator and .NET diagnostic rules, a Directory.Build.props for centralized build settings, and GitHub Actions workflows for CI, SonarCloud, CodeQL, and DevSkim analysis.

(repo-wide) · high confidence

Initial release of Shop.PublicApi with .NET 10, health checks, and security hardening

This entry introduces the Shop.PublicApi project, bootstrapped on .NET 10 (Azure Linux 3.0) and containerized via a new Dockerfile. The API exposes a /health endpoint for monitoring and configures HSTS and secure headers (including X-Frame-Options and removal of server signatures) for production security. It integrates Scalar for API documentation, MiniProfiler for performance monitoring, and FluentValidation with English localization. The service also supports response compression, API versioning, and correlation IDs, with development settings for caching and logging provided in appsettings files.

src/Shop.PublicApi · high confidence

Initializes query-side service registration and MongoDB configuration

Adds the \ConfigureServices.cs\ file in \src/Shop.Query\ to centralize the setup of the read-side infrastructure. This includes registering MediatR handlers, AutoMapper mappings, and FluentValidation validators from the query assembly, as well as configuring the \NoSqlDbContext\ as a scoped service for MongoDB interactions. It also sets up specific MongoDB conventions (such as camel-case element names and enum string representation) and registers the \CustomerReadOnlyRepository\.

src/Shop.Query · high confidence

Introduce CustomerQueryModel for customer data queries

A new CustomerQueryModel class has been added to the Shop.Query.QueriesModel namespace to serve as a read-model for customer data. This sealed class implements IQueryModel\<Guid\> and exposes customer details including Id, FirstName, LastName, Gender, Email, and DateOfBirth via immutable properties with private init accessors. It also provides a computed FullName property that concatenates the first and last names, and includes a private parameterless constructor likely for serialization purposes.

src/Shop.Query/QueriesModel · high confidence

Introduce EF Core infrastructure with compiled queries and explicit caching services

The Shop.Infrastructure layer now provides the foundational data access and caching services. Entity Framework Core is configured via a BaseDbContext that disables lazy loading, enforces VARCHAR(255) for strings, and restricts cascade deletes to improve data integrity and performance. Repositories utilize compiled queries for high-frequency lookups, such as checking customer email uniqueness. The UnitOfWork orchestrates database transactions and publishes domain events in parallel. Additionally, explicit DI registrations are provided for both in-memory and distributed caching implementations.

src/Shop.Infrastructure · high confidence

Introduce MongoDB-backed NoSqlDbContext for query data access

A new NoSqlDbContext class has been added to the Shop.Query project to handle interactions with a MongoDB database. This context implements IReadDbContext and ISynchronizeDb, providing methods to retrieve collections, create collections with strict validation, and manage indexes (specifically on the CustomerQueryModel Email field). It supports upsert and delete operations with a built-in retry policy that handles transient MongoDB exceptions using exponential backoff with jitter. The class also implements IDisposable to ensure proper cleanup of the underlying MongoClient resources.

src/Shop.Query/Data/Context · high confidence

Introduce core domain primitives and repository abstractions

The SharedKernel layer now provides the foundational building blocks for the application's domain model and data access. This includes abstract base classes for entities (BaseEntity) and domain events (BaseEvent), along with interfaces defining the contract for write-only repositories (IWriteOnlyRepository), event store repositories (IEventStoreRepository), and caching services (ICacheService). These additions establish the standard patterns for entity identity, event tracking, and data persistence within the core module.

src/Shop.Core/SharedKernel · high confidence

Introduce core extension methods and switch JSON serialization to System.Text.Json

This change introduces a new set of extension methods in the Shop.Core library to simplify common operations. JsonExtensions now uses System.Text.Json instead of Newtonsoft.Json, featuring a custom resolver that allows deserialization of types with private constructors. Additional extensions include AssemblyExtensions for retrieving types implementing specific interfaces, ConfigurationExtensions and ServiceProviderExtensions for retrieving strongly-typed configuration options, and GenericTypeExtensions for checking default values and getting generic type names.

src/Shop.Core/Extensions · high confidence

Introduces query-side abstractions for MongoDB read models and synchronization

Adds a new set of interfaces in the Shop.Query.Abstractions layer to define the contract for read-only data access and model synchronization. This includes IQueryModel and IQueryMarker to identify query models, IReadOnlyRepository for fetching entities by ID, and IReadDbContext to access MongoDB collections and manage connection strings. Additionally, ISynchronizeDb provides methods to upsert and delete query models, while IReadDbMapping allows for configuring database mappings. These interfaces establish the foundation for the CQRS read side, separating query logic from the domain model.

src/Shop.Query/Abstractions · high confidence

Introduction of Customer domain entity with soft-delete and email uniqueness support

The Customer aggregate root is now defined with a public constructor for creation and a parameterless constructor for ORM persistence. It includes properties for name, gender, email, and date of birth, along with a method to update the email address. A new soft-delete capability has been added via a Delete method that sets an internal deletion flag and raises a domain event. The EGender enum is configured for JSON string serialization. Additionally, a write-only repository interface is introduced, providing methods to check for email uniqueness, including an overload that excludes the current customer from the check.

src/Shop.Domain/Entities/CustomerAggregate · high confidence

New Customers API controller with cancellation support

The Public API now exposes a new CustomersController at /api/customers that handles full CRUD operations for customer entities. The Create endpoint returns HTTP 201 Created upon success, while Update, Delete, GetById, and GetAll return HTTP 200 OK. All actions accept a CancellationToken to support request cancellation.

src/Shop.PublicApi/Controllers · high confidence

New command and validator classes for customer CRUD operations

The application layer now includes dedicated command and validator classes for creating, updating, and deleting customers. CreateCustomerCommand defines the input fields (name, gender, email, date of birth) with validation rules, while UpdateCustomerCommand handles email updates and DeleteCustomerCommand targets customer removal by ID. Each command is paired with a FluentValidation validator to enforce data integrity before processing.

src/Shop.Application/Customer/Commands · high confidence

New configuration models for cache and connection settings

The application now exposes dedicated configuration classes for cache and connection settings. CacheOptions defines absolute and sliding expiration times for caching behavior, while ConnectionOptions manages SQL, non-SQL, and cache connection strings, including a helper method to detect in-memory cache usage. These models implement IAppOptions to support structured configuration binding.

src/Shop.Core/AppSettings · high confidence

New core configuration and email validation utilities

The Shop.Core module now includes a new ConfigureServices class that registers ConnectionOptions and CacheOptions with data annotation validation, ensuring configuration is validated at startup. Additionally, a new RegexPatterns class provides a pre-compiled, culture-invariant regular expression for validating email addresses, supporting special characters in the local part.

src/Shop.Core · high confidence

New read-only repository layer for customer queries

The query-side data access now includes a dedicated read-only repository structure for customer data. A new \ICustomerReadOnlyRepository\ interface and its \CustomerReadOnlyRepository\ implementation have been added, inheriting from a new \BaseReadOnlyRepository\ base class. This base class handles generic read operations against MongoDB using an \IReadDbContext\. The specific customer repository implements \GetAllAsync\, which retrieves all customers sorted by first name ascending and date of birth descending. This change isolates read-specific logic from the domain layer, providing a clean abstraction for querying customer data.

src/Shop.Query/Data/Repositories · high confidence

New standardized API response models

The API now uses a new set of model classes (\ApiResponse\, \ApiResponse\<TResult\>\, and \ApiErrorResponse\) to structure HTTP responses. These models provide a consistent format including success status, HTTP status codes, success messages, and error details, with factory methods for common scenarios like OK, BadRequest, Unauthorized, Forbidden, NotFound, and InternalServerError. The generic \ApiResponse\<TResult\>\ also supports returning typed results with a specific Created (201) status.

src/Shop.PublicApi/Models · high confidence

Behavioural changes

Application services now register via a dedicated marker interface with singleton validators

The application layer now uses a new IApplicationMarker interface to identify the assembly for service registration, replacing previous assembly-discovery methods. In ConfigureServices, validators are explicitly registered with a Singleton lifetime, and MediatR handlers are registered from the same marker-identified assembly, ensuring consistent service management and improved testability.

src/Shop.Application · high confidence

Centralized error handling for unhandled exceptions

The API now includes an ErrorHandlingMiddleware that catches unhandled exceptions during request processing. In production, it returns a standardized JSON error response to clients, while in development environments, it exposes the full exception details to aid debugging. This ensures consistent error reporting and prevents raw stack traces from leaking to end users in live deployments.

src/Shop.PublicApi/Middlewares · high confidence

Customer command handlers now enforce email uniqueness and return standard HTTP status codes

The application's customer command handlers (Create, Update, Delete) now validate that the provided email address is unique before persisting changes, returning an error if a duplicate is found. Additionally, the CreateCustomerCommandHandler now returns a 201 Created status with the new customer's location, replacing the previous 200 OK response, while Update and Delete handlers return success messages upon completion.

src/Shop.Application/Customer/Handlers · high confidence

Customer event handlers now synchronize query models and clear cache

The new CustomerEventHandler in the Shop.Query layer processes CustomerCreated, CustomerUpdated, and CustomerDeleted domain events. For create and update events, it maps the domain entity to a CustomerQueryModel and persists it via ISynchronizeDb.UpsertAsync; for delete events, it removes the corresponding query model by email. In all cases, it invalidates relevant cache entries (GetAllCustomerQuery and GetCustomerByIdQuery) to ensure query-side data consistency.

src/Shop.Query/EventHandlers · high confidence

Customer query handlers now use caching and validation

The \GetAllCustomerQueryHandler\ and \GetCustomerByIdQueryHandler\ in the Shop.Query application layer have been implemented to improve performance and data integrity. The \GetAllCustomerQueryHandler\ now caches the list of all customers using a shared cache key to avoid repeated database calls. The \GetCustomerByIdQueryHandler\ integrates FluentValidation to validate requests before processing and also utilizes caching for individual customer lookups, returning a specific 'not found' result if the customer does not exist.

src/Shop.Query/Application/Customer/Handlers · high confidence

CustomerFactory adds input validation for email creation

The CustomerFactory now validates email addresses during customer creation. The new Create method accepts raw string inputs and uses the Email value object to validate the email format; if validation fails, it returns a Result with an error list instead of creating the customer. A second overload remains for cases where a pre-validated Email value object is already available.

src/Shop.Domain/Factories · high confidence

Enforce required field validation for customer query models

The CustomerMap configuration now enforces that the Id, FirstName, LastName, Gender, Email, and DateOfBirth fields are mandatory when deserializing customer data. This ensures that incomplete customer records are rejected during query operations, improving data integrity for read-side models.

src/Shop.Query/Data/Mappings · high confidence

Refactored Customer domain events to use a base class hierarchy

The Customer aggregate's domain events (Created, Updated, Deleted) now inherit from a new abstract CustomerBaseEvent, which centralizes common properties like Id, name, gender, email, and date of birth. This structural change simplifies the event definitions by removing redundant property declarations from each specific event class, ensuring consistent data exposure across all customer lifecycle events.

src/Shop.Domain/Entities/CustomerAggregate/Events · high confidence

Standardized API startup configuration and error handling

The public API now uses a unified startup pipeline that automatically validates and compiles AutoMapper mappings, applies database migrations for SQL Server and MongoDB, and initializes distributed caching (Redis or in-memory). It also introduces a global error-handling middleware and standardizes HTTP response codes, mapping domain results to specific status codes like 201 Created for new resources and 403 Forbidden for access denials.

src/Shop.PublicApi/Extensions · high confidence

Unified customer event mapping to query model

The query layer now maps CustomerCreated, CustomerUpdated, and CustomerDeleted events to the CustomerQueryModel using a single generic factory method. This ensures consistent construction of the query model from any customer base event, simplifying the mapping configuration and reducing duplication in the EventToQueryModelProfile.

src/Shop.Query/Profiles · high confidence

Updated database migration snapshots to reflect Entity Framework Core 9 and schema refinements

The migration model snapshots and new migration files in the Shop.PublicApi have been updated to target .NET 9 (ProductVersion 9.0.0). This includes a schema change for the Customers table where the Gender column type has been switched from varchar(6) to nvarchar(6), and a significant reduction in the EventStores table's Data column size from VARCHAR(MAX) to varchar(255). Additionally, the entity namespace for Customer has been updated from Shop.Domain.Entities.Customer to Shop.Domain.Entities.CustomerAggregate.

src/Shop.PublicApi/Migrations · high confidence

Test coverage

Added SQLite test fixture for unit tests; Added integration tests for the Customers API controller; Added test helper extensions for Faker and HTTP requests; Added unit tests for CreateCustomerCommandValidator; Added unit tests for Customer entity domain events; Added unit tests for CustomerFactory; Added unit tests for Email value object validation; Added unit tests for core extension methods and shared kernel components; Added unit tests for customer command handlers; Added unit tests for customer query validation and handlers.

Dependencies

Migrate to .NET 10 and centralize NuGet package management

The solution has been upgraded to target .NET 10.0 across all projects (Shop.Core, Shop.Domain, Shop.Application, Shop.Infrastructure, Shop.PublicApi, Shop.Query, and test projects). To simplify dependency maintenance, a new Directory.Packages.props file introduces central package version management with transitive pinning and floating versions. Key library updates include moving to FluentValidation 12.\, NSubstitute 6.2.\, xUnit 2.9.\, and MongoDB Driver 3.12.\, while removing legacy references like Newtonsoft.Json in favor of System.Text.Json.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.

Score

  • CAI 63 → 57 (-5.9)
  • Rubric changed (rubric-2026.08.18 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 68 → 59 (-8.3)
  • Architecture 91 → 91 (-0.0)
  • Maturity 68 → 69 (+1.1)
  • Readiness 55 → 47 (-7.9)
  • Security 69 → 75 (+5.2)
  • Domain Modelling 77 → 67 (-10.2)
  • Event-Driven 100 → 100 (+0.0)

Resolved (20)

  • Bounded contexts not declared
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • No exposed public API
  • NoWarnInCsproj (Directory.Build.props)
  • Small-team knowledge concentration
  • redundant comment (src/Shop.Infrastructure/Data/Extensions/EntityTypeBuilderExtensions.cs)

New (41)

  • CoverageExclusion (src/Shop.Application/ConfigureServices.cs)
  • CoverageExclusion (src/Shop.Core/ConfigureServices.cs)
  • CoverageExclusion (src/Shop.Infrastructure/ConfigureServices.cs)
  • CoverageExclusion (src/Shop.PublicApi/Extensions/ServicesCollectionExtensions.cs)
  • CoverageExclusion (src/Shop.Query/ConfigureServices.cs)
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no project overview (README.md)
  • Duplicated block (13 lines × 2) (src/Shop.Application/Customer/Handlers/DeleteCustomerCommandHandler.cs)
  • EmptyCatchBlock (src/Shop.Query/ConfigureServices.cs)
  • High IaC: WD-COMPOSE-0002 (docker-compose.yml)
  • High IaC: WD-COMPOSE-0002 (docker-compose.yml)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • …and 21 more

Changes since last survey

  • 8 commits — 8 feature/other, 0 fixes

By area

  • (root) — 4 commits
  • .github/workflows — 3 commits
  • src/Shop.PublicApi — 1 commit

Notable commits

  • change: chore: add CancellationToken support to customer controller actions
  • change: chore: update Roslynator package versions and xUnit analyzers
  • change: chore: update actions versions in workflow files and add CancellationToken to error handling middleware
  • change: chore: update package versions and suppress additional warnings
  • change: chore: update package versions for FluentAssertions, MongoDB, Polly, and xUnit
  • change: chore: update package versions for Microsoft.OpenApi, MongoDB, and NSubstitute
  • change: ci: update JDK distribution to sapmachine in workflow
  • change: ci: update JDK setup in SonarCloud workflow

API surface

  • Unchanged — 5 HTTP endpoints

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

jeangatto/ASP.NET-Core-Clean-Architecture-CQRS-Event-Sourcing was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 20 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 6cfe1f2eb65afc4d58a4075feb42785e4889236d — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-28e75b8e3254.