Skip to main content

Architecture

The Ordering service is built using Domain-Driven Design (DDD) and Clean Architecture principles, organized into four distinct layers:
Ordering.API/           # Presentation Layer - Minimal APIs with Carter
Ordering.Application/   # Application Layer - CQRS, Commands, Queries, Event Handlers
Ordering.Domain/        # Domain Layer - Entities, Value Objects, Domain Events
Ordering.Infrastructure/ # Infrastructure Layer - EF Core, Persistence

Core Responsibilities

  • Order Management: Create, update, delete, and query orders
  • Domain Logic: Enforce business rules through rich domain models
  • Event Processing: Consume BasketCheckout events and publish order events
  • Data Persistence: Store orders using Entity Framework Core with SQL Server
  • CQRS Implementation: Separate read and write operations with MediatR

Technology Stack

Frameworks & Libraries

  • .NET 8: Latest LTS version for high performance
  • Entity Framework Core: ORM for database access
  • MediatR: CQRS pattern implementation
  • Carter: Minimal API endpoint organization
  • MassTransit: Message broker integration
  • FluentValidation: Input validation
  • Mapster: Object-to-object mapping

Patterns & Practices

  • Domain-Driven Design (DDD): Rich domain models with aggregates
  • Clean Architecture: Dependency inversion and separation of concerns
  • CQRS: Command Query Responsibility Segregation
  • Repository Pattern: Data access abstraction
  • Domain Events: Decoupled business logic
  • Value Objects: Immutable domain primitives
  • Aggregate Root: Order as the consistency boundary

Project Structure

Domain Layer

Ordering.Domain/
├── Abstractions/
│   ├── Entity.cs           # Base entity with audit fields
│   ├── Aggregate.cs        # Base aggregate with domain events
│   ├── IDomainEvent.cs     # Domain event marker interface
│   └── IAggregate.cs       # Aggregate marker interface
├── Models/
│   ├── Order.cs            # Order aggregate root
│   ├── OrderItem.cs        # Order line item entity
│   ├── Customer.cs         # Customer entity
│   └── Product.cs          # Product entity
├── ValueObjects/
│   ├── OrderId.cs          # Strongly-typed order identifier
│   ├── OrderName.cs        # Order name with validation
│   ├── CustomerId.cs       # Strongly-typed customer identifier
│   ├── ProductId.cs        # Strongly-typed product identifier
│   ├── Address.cs          # Address value object
│   └── Payment.cs          # Payment value object
├── Events/
│   ├── OrderCreatedEvent.cs  # Domain event for order creation
│   └── OrderUpdatedEvent.cs  # Domain event for order updates
├── Enums/
│   └── OrderStatus.cs      # Order status enumeration
└── Exceptions/
    └── DomainException.cs  # Domain-specific exceptions

Application Layer

Ordering.Application/
├── Orders/
│   ├── Commands/
│   │   ├── CreateOrder/    # Create order command & handler
│   │   ├── UpdateOrder/    # Update order command & handler
│   │   └── DeleteOrder/    # Delete order command & handler
│   ├── Queries/
│   │   ├── GetOrders/      # Get all orders with pagination
│   │   ├── GetOrdersByCustomer/  # Get orders by customer ID
│   │   └── GetOrdersByName/      # Search orders by name
│   └── EventHandlers/
│       ├── Domain/
│       │   ├── OrderCreatedEventHandler.cs   # Publishes integration event
│       │   └── OrderUpdatedEventHandler.cs   # Logs update event
│       └── Integration/
│           └── BasketCheckoutEventHandler.cs # Consumes basket checkout
├── Dtos/
│   ├── OrderDto.cs         # Order data transfer object
│   ├── OrderItemDto.cs     # Order item DTO
│   ├── AddressDto.cs       # Address DTO
│   └── PaymentDto.cs       # Payment DTO
├── Data/
│   └── IApplicationDbContext.cs  # DbContext interface
└── DependencyInjection.cs  # Service registration

Infrastructure Layer

Ordering.Infrastructure/
├── Data/
│   ├── ApplicationDbContext.cs       # EF Core DbContext
│   ├── Configurations/
│   │   ├── OrderConfiguration.cs    # Order entity configuration
│   │   ├── OrderItemConfiguration.cs # Order item configuration
│   │   ├── CustomerConfiguration.cs  # Customer configuration
│   │   └── ProductConfiguration.cs   # Product configuration
│   ├── Interceptors/
│   │   ├── DispatchDomainEventsInterceptor.cs  # Publishes domain events
│   │   └── AuditableEntityInterceptor.cs       # Sets audit fields
│   ├── Migrations/                   # EF Core migrations
│   └── Extensions/
│       ├── DatabaseExtensions.cs     # Database initialization
│       └── InitialData.cs            # Seed data
└── DependencyInjection.cs            # Infrastructure service registration

API Layer

Ordering.API/
├── Endpoints/
│   ├── CreateOrder.cs      # POST /orders
│   ├── UpdateOrder.cs      # PUT /orders
│   ├── DeleteOrder.cs      # DELETE /orders/{id}
│   ├── GetOrders.cs        # GET /orders
│   ├── GetOrdersByCustomer.cs  # GET /orders/customer/{customerId}
│   └── GetOrdersByName.cs      # GET /orders/{orderName}
├── Program.cs              # Application entry point
└── DependencyInjection.cs  # API service registration

Service Dependencies

// Application Layer Registration
public static IServiceCollection AddApplicationServices(
    this IServiceCollection services, IConfiguration configuration)
{
    services.AddMediatR(config =>
    {
        config.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
        config.AddOpenBehavior(typeof(ValidationBehavior<,>));
        config.AddOpenBehavior(typeof(LoggingBehavior<,>));
    });

    services.AddFeatureManagement();
    services.AddMessageBroker(configuration, Assembly.GetExecutingAssembly());

    return services;
}
// Infrastructure Layer Registration
public static IServiceCollection AddInfrastructureServices(
    this IServiceCollection services, IConfiguration configuration)
{
    var connectionString = configuration.GetConnectionString("Database");

    services.AddScoped<ISaveChangesInterceptor, AuditableEntityInterceptor>();
    services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();

    services.AddDbContext<ApplicationDbContext>((sp, options) =>
    {
        options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
        options.UseSqlServer(connectionString);
    });

    services.AddScoped<IApplicationDbContext, ApplicationDbContext>();

    return services;
}

Integration with Other Services

Message Consumption

Consumes: BasketCheckoutEvent from Basket service
  • Triggers order creation when a customer checks out their basket
  • Maps basket data to order aggregate
  • Initiates order fulfillment process

Message Publishing

Publishes: OrderDto (Order Created Integration Event)
  • Published when a new order is created
  • Feature flag controlled: OrderFullfilment
  • Can be consumed by fulfillment, notification, or other services

Database Schema

The service uses SQL Server with Entity Framework Core, storing:
  • Orders: Order aggregate roots with shipping, billing, and payment info
  • OrderItems: Line items within orders
  • Customers: Customer information
  • Products: Product reference data
All entities include audit fields (CreatedAt, CreatedBy, LastModified, LastModifiedBy) automatically populated by the AuditableEntityInterceptor.

Key Features

Domain Event Handling

Domain events are dispatched automatically during SaveChanges through the DispatchDomainEventsInterceptor, ensuring:
  • Events are published only when changes are persisted
  • Transaction consistency between state changes and events
  • Decoupled domain logic through event handlers

Validation

Multi-layer validation ensures data integrity:
  • Domain Layer: Business rule validation in aggregates and value objects
  • Application Layer: FluentValidation validators on commands
  • Value Objects: Guard clauses prevent invalid state

CQRS Separation

  • Commands: Modify state, return simple results (CreateOrderCommand, UpdateOrderCommand)
  • Queries: Read-only operations with optimized projections (GetOrdersQuery, GetOrdersByCustomerQuery)
  • MediatR: Mediates between API endpoints and handlers

Next Steps

Build docs developers (and LLMs) love