finds.dev← search

// the find

kgrzybek/sample-dotnet-core-cqrs-api

★ 3,053 · C# · MIT · updated Feb 2024

Sample .NET Core REST API CQRS implementation with raw SQL and DDD using Clean Architecture.

A reference implementation showing CQRS with DDD in .NET, using EF Core for writes and raw Dapper SQL for reads, wired together with MediatR and structured around Clean Architecture layers. Aimed at developers trying to understand how these patterns fit together in practice rather than just in theory. The domain is deliberately simple (customers, orders, payments) so the architecture is the point.

- The read/write model split is actually implemented properly: Dapper hits database views for queries, EF Core manages the domain graph for commands. This is a rare concrete example rather than a hand-wavy description.

- Outbox pattern and internal commands are both implemented end-to-end with Quartz.NET, showing how to handle domain events reliably across process boundaries without just waving at 'use a message bus'.

- Strongly-typed IDs (CustomerId, OrderId, etc.) with EF Core value converters are a pattern many teams need but struggle to wire up—the implementation here is reusable and correct.

- Integration tests actually hit a real database rather than mocking everything, which means the SQL, EF mappings, and domain logic are all exercised together.

- Last meaningful update was early 2024 and it still targets patterns from .NET 5/6 era—no minimal APIs, no .NET 8 keyed services, Autofac DI wiring feels dated compared to current practices.

- Only integration tests and a handful of unit tests exist. The test coverage is thin; the Orders and Customers test files cover happy paths almost exclusively with no edge-case or failure scenario tests.

- The domain itself is trivially small. There are no bounded context boundaries, no aggregate-to-aggregate communication challenges, and no examples of eventual consistency handling that would appear in a real system using this architecture.

- Manual SQL scripts for database initialization with no migration strategy means any schema change is a manual exercise—there's no guidance on how to evolve the schema in a real project, which is one of the harder problems with this dual-model approach.

View on GitHub → Homepage ↗

// want more like this?

We dig through GitHub every week and send a few repos picked for what you actually care about — each with an honest take like this one.

Get finds in your inbox → Search again →