A practical, real-world Domain-Driven Design (DDD), Clean Architecture, and CQRS Example: Basket API π
A practical, real-world clean architecture, Domain-Driven Design (DDD) application with more than 20 design patterns for managing shopping basket operations.
The Basket Bounded Context application is a real-world example of Domain-driven Design (DDD), implementing patterns like Command Query Responsibility Segregation (CQRS) and adhering to Clean Architecture principles. It is responsible for managing the lifecycle of shopping baskets, including:
- Creating baskets
- Adding items
- Updating item quantities
- Removing items
- Managing Coupons
- Applying discounts and coupons
Built with C#, this application demonstrates how to structure software with DDD, CQRS, and Clean Architecture, ensuring maintainability, scalability, and adherence to domain-driven practices.
- Features
- Event Storming
- Clean Architecture Overview
- Interaction Flow in DDD and Clean Architecture
- Domain-Driven Design Principles and Patterns
- Design Patterns Used
- Technologies Used
- Getting Started
- Project Structure
- Usage
- Watch and Learn
- About the Author
- Contributing
- License
-
Basket Management:
- Create baskets with basket items.
- Underlying architecture for Add, update, and remove items.
- Underlying architecture for Calculate total amounts including tax and shipping.
-
Coupon System:
- Underlying architecture for Apply and remove discount coupons.
- Underlying architecture for Support for fixed and percentage-based discounts.
-
Clean Architecture:
- Separation of concerns with clearly defined layers.
- Independent and replaceable infrastructure.
-
Domain-Driven Design:
- Focus on business rules encapsulated in the domain layer.
- Events for tracking domain state changes.
- Tactical and Strategical DDD patterns:
Ubiquitous Language,Bounded Context,Value Objects,Entities,Aggregates,Domain Events,Domain Services,Application services,Repositories,Factories,Modules.
-
Cross-Cutting Concerns:
-
Logging: Centralized and consistent logging for debugging and monitoring.
-
Validation: Reusable validation logic using FluentValidation to ensure data integrity.
-
Exception Handling: Unique exception handling to provide meaningful error messages and prevent crashes.
View the Event Storming Board on Miro
BasketContext is structured around the Clean Architecture principles:
-
Domain Layer:
- Contains the core business logic.
- Aggregate Roots:
Basket,Coupon. - Entities:
BasketItem,Customer,Seller. - Value Objects:
Quantity,Amount. - Domain Events:
BasketItemAddedEvent,BasketItemRemovedEventetc. - Domain Services:
ISellerLimitService,ICouponService. - Factories : the factories for the most of domain aggregate and entities with Test factories
-
Application Layer:
- Use cases for basket operations (e.g.,
AddItemToBasketCommand). - Interfaces for services like
ICouponService.
- Use cases for basket operations (e.g.,
-
Infrastructure Layer:
- Handles persistence with Entity Framework Core.
- Mapping between domain models and database entities.
-
API Layer:
- Exposes HTTP endpoints for basket operations.
- Built with ASP.NET Core.
The interaction flow between components in our system is designed with DDD principles and Clean Architecture. Below is a detailed flow diagram:
sequenceDiagram
participant User
participant WebAPI
participant ApplicationLayer
participant DomainModel
participant EventDispatcher
participant DomainEventHandler
participant IntegrationEventHandler
User->>WebAPI: Sends Request
WebAPI->>ApplicationLayer: Forwards Request (CQRS)
ApplicationLayer->>DomainModel: Acts on Domain Aggregates and Entities
DomainModel-->>EventDispatcher: Raises Domain Events
EventDispatcher->>DomainEventHandler: Publishes Events
DomainEventHandler-->>IntegrationEventHandler: Maps to Integration Events (if needed)
-
Entities:
- Rich domain models encapsulating business rules.
- Example:
Basketensures valid operations like adding items and calculating totals.
-
Value Objects:
- Immutable objects representing concepts like
Quantity.
- Immutable objects representing concepts like
-
Aggregates:
- Aggregate root:
BasketandCouponAggregate roots manage consistency across related entities.
- Aggregate root:
-
Domain Events:
- Notify systems of domain changes (e.g.,
BasketItemDeactivatedEvent,BasketItemCoundUpdatedEvent).
- Notify systems of domain changes (e.g.,
-
Integration Events:
- Notify external systems of domain changes (e.g.,
BasketItemAddedEvent).
- Notify external systems of domain changes (e.g.,
-
Domain Services:
- Encapsulate complex business logic that doesn't naturally fit within an entity or value object.
- Example:
ICouponServicevalidates and applies coupons to a basket.
-
Modules:
- Group related aggregates, entities, and services into cohesive units.
- Example: The
BasketModuleencapsulates all operations related to managing a shopping basket.
-
Application Services:
- Coordinate application workflows by interacting with domain and infrastructure layers.
- Example:
AddItemToBasketCommandHandlerorchestrates the addition of items to a basket.
-
Factories:
- Provide centralized logic for creating complex domain objects while ensuring invariants are upheld.
- Example:
BasketFactorysimplifies the creation of aBasketwith default settings and dependencies.
-
Repositories:
- Interfaces for interacting with aggregates.
- Example:
IBasketRepositoryabstracts the persistence ofBasketentities.
-
Ubiquitous Language:
- Shared terminology between developers, domain experts, and stakeholders.
- Example: Terms like
Basket,BasketItem,Coupon, andSellerare part of the shared vocabulary.
-
Bounded Context:
- Defines the boundary within which the domain model is valid and consistent.
- Example: The
BasketContextfocuses solely on shopping basket operations and is separate from contexts likeInventoryorPayment.
This repository showcases the use of several well-known design patterns, providing real-world examples of how they can be applied in a Domain-Driven Design (DDD) and Clean Architecture context:
- Description: Separates the read and write responsibilities to improve scalability and maintainability.
- Usage: Command handlers are used for state-changing operations, while query handlers handle data retrieval.
- Description: Encapsulates success or failure outcomes, ensuring error handling is explicit and consistent.
- Usage: Used in handler methods to return meaningful results with error messages.
- Description: Dynamically adds responsibilities to objects without altering their structure.
- Usage: Enhances validation and business rules in the domain layer.
- Description: Simplifies communication between components by centralizing interactions.
- Usage: Implements MediatR for handling commands and queries in a decoupled manner.
- Description: Enables asynchronous communication between components through event publishing and handling.
- Usage: Domain events trigger integration events that are published to Kafka.
- Description: Enables selecting algorithms or behaviors at runtime.
- Usage: Implements different discount strategies, such as percentage or fixed discounts.
- Description: Defines the skeleton of an algorithm, allowing subclasses to override specific steps.
- Usage:
CommandHandlerBase,ValueObjectetc.
- Description: Provides a way to instantiate objects while abstracting the creation logic.
- Usage: Ensures domain objects like
BasketandBasketItemare created with all invariants upheld. Test project uses multiple factories.
- Description: Passes requests along a chain of handlers until one handles it.
- Usage: Used for applying a series of validation or business rules sequentially.
- Description: Ensures multiple operations on a database are performed as a single transaction.
- Usage: Manages database interactions in the infrastructure layer, ensuring consistency.
- The repository demonstrates other patterns and principles, showcasing best practices in software design and development.
By using these patterns, the Basket Bounded Context provides a real-world implementation of clean and maintainable software architecture, ensuring scalability and adherence to design principles.
- C#: The core programming language.
- ASP.NET Core: Web framework for building APIs.
- Entity Framework Core: Object-relational mapping (ORM) tool for database interactions.
- MediatR: Library for implementing the mediator pattern, enabling decoupled communication between application components.
- AutoMapper: Library for object-to-object mapping, simplifying the transformation of data transfer objects.
- FluentValidation: Library for building strongly-typed, reusable validation rules.
- Ardalis.SmartEnum: Library for managing enums with added functionality, such as encapsulating logic related to each value.
- csharpfunctionalextensions: Library for functional programming patterns. Only
Resultpattern used from this package. - Scrutor: Library for assembly scanning and service registration in .NET Dependency Injection.
- Confluent.Kafka: Library for integrating with Apache Kafka, enabling message-based communication and event streaming.
- Docker: Containerization tool for packaging applications and dependencies.
- Install Docker Desktop and ensure it is running.
- Install Visual Studio 2022 with Docker support enabled.
- Install .NET SDK 6.0 or later.
- Install and start SQL Server 2019 or later.
- Install and start Apache Kafka with Zookeeper.
- Optionally, install Kafka UI for managing Kafka topics.
-
Clone the Repository:
git clone https://github.com/TuralSuleymani/the-real-DDD-CQRS-CleanArchitecture.git
-
Set Up Environment: Run existing docker-compose file to start Kafka and SQL Server via the command:
docker-compose up -d
or run it from Visual Studio.
-
Run the Application:
dotnet run --project src/VOEConsulting.Flame.BasketContext.Api
βββ src/
β βββ VOEConsulting.Flame.BasketContext.Api/ # API Layer
β βββ VOEConsulting.Flame.BasketContext.Application/ # Application Layer
β βββ VOEConsulting.Flame.BasketContext.Domain/ # Domain Layer
β βββ VOEConsulting.Flame.BasketContext.Infrastructure/ # Infrastructure Layer
βββ VOEConsulting.Flame.Common.Core/ # For Shared elements
βββ VOEConsulting.Flame.Common.Domain/ # Contains command domain logic elements like Entity,ValueObject,AggregateRoot,Event etc.
βββ tests/
β βββ VOEConsulting.Flame.BasketContext.Tests.Unit/ # Unit Tests
β βββ VOEConsulting.Flame.BasketContext.Tests.Data/ # Test Data
βββ README.md
Endpoint: POST /api/baskets
{
"taxPercentage": "18",
"customer": {
"Id": "e15d6da4-d7e4-46ad-6e5b-f735842a2546",
"isEliteMember": false
}
}Endpoint: POST /api/baskets/{basketId}/items
{
"basketId": "5739c2ae-019a-420d-9464-26753371dfe6",
"seller": {
"id": "123e4567-e89b-12d3-a456-426614174001",
"name": "Seller Name",
"rating": 4.5,
"shippingLimit": 100.0,
"shippingCost": 10.0
},
"basketItem": {
"itemId": "323e4567-e89b-12d3-a456-426614174002",
"name": "Hsn 5",
"imageUrl": "https://example.com/item-image2.jpg",
"limit": 17
},
"quantity": {
"value": 2,
"quantityLimit": 14,
"pricePerUnit": 24.5
}
}
To better understand Domain-Driven Design (DDD), Clean Architecture, and the concepts mentioned in this repository, check out these related videos from my YouTube channel:
-
Video explanation of current github repo with details!

Learn more about current github repository; I'm explanation DDD(Domain-driven Design) and Clean Architecture behind this github project. -
Podcast with Robert C. Martin: The Creator of Clean Architecture and SOLID Principles

In this episode, I sit down with Robert C. Martin (Uncle Bob), the legendary author of Clean Code and Clean Architecture and creator of the SOLID principles, to explore Object-Oriented Programming, software design, and the future of development. -
Podcast with Mads Torgersen: The Man Behind the C# Language

Explore the evolution of C#, its features, and its application in modern software development with the Lead Designer of C# language and of course, with me :). -
Podcast with Rebecca Wirfs-Brock: The Creator of Responsibility-Driven Design

In this podcast, I had the privilege to host legendary Rebecca Wirfs-Brock, where we discussed about Domain-Driven Design, Responsibility-driven design, and design heuristics. -
A Deep Dive into Architecture, Functional Programming, and Dependency Injection with Mark Seemann

In this episode, I chat with renowned software architect Mark Seemann about modern software development, covering topics like well-designed architecture, Dependency Injection, and functional programming. -
An Elegant Introduction to Domain-Driven Design (DDD) and Its Patterns

My speech at Software Architecture Conference, where I provided an overview of DDD principles, their patterns, and how they can be applied in real-world projects. -
Functional programming in C# with Railway-Oriented Programming

A detailed explanation of Functional Programming in C#, its use cases,values, and implementation using Railway-Oriented programming -
Master the Result Pattern: The One Video You Must Watch

A detailed explanation of the Result pattern, its use cases, and implementation in C# projects.
If you found this project helpful or have any questions, feel free to connect with me:
Don't forget to β this repo if you like it!
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch (
feature/your-feature). - Commit your changes.
- Push the branch.
- Open a pull request.
This project is licensed under the MIT License. See the LICENSE file for details.
