Easy📖Теория2 min

Архитектура проекта Order Management System

Структура решения на .NET 10, распределение ответственности между слоями, правило направления зависимостей и технологический стек

Архитектура проекта Order Management System

В этой статье мы разберём архитектуру системы управления заказами (Order Management System) -- веб-приложения на C# .NET 10, построенного на принципах Domain-Driven Design, SOLID и Clean Architecture.

Обзор системы

Order Management System -- это REST API для управления заказами, которое демонстрирует промышленный подход к построению корпоративных приложений. Система позволяет создавать заказы, добавлять позиции, подтверждать и отменять их, а также развёртываться в облаке Azure.

Проект организован в виде .NET Solution, содержащего несколько проектов -- каждый отвечает за свой архитектурный слой.

Структура решения

OrderManagement/
├── src/
│   ├── OrderManagement.Domain/           # Ядро -- бизнес-логика
│   │   ├── Entities/
│   │   ├── ValueObjects/
│   │   ├── Aggregates/
│   │   ├── Events/
│   │   ├── Enums/
│   │   ├── Exceptions/
│   │   └── Interfaces/
│   │
│   ├── OrderManagement.Application/      # Use Cases -- прикладная логика
│   │   ├── Commands/
│   │   ├── Queries/
│   │   ├── DTOs/
│   │   ├── Interfaces/
│   │   ├── Behaviors/
│   │   └── Mappings/
│   │
│   ├── OrderManagement.Infrastructure/   # Реализация -- БД, внешние сервисы
│   │   ├── Persistence/
│   │   ├── Repositories/
│   │   ├── Services/
│   │   └── Configuration/
│   │
│   └── OrderManagement.API/              # Точка входа -- Web API
│       ├── Controllers/
│       ├── Middleware/
│       ├── Filters/
│       └── Program.cs
│
├── tests/
│   ├── OrderManagement.Domain.Tests/
│   ├── OrderManagement.Application.Tests/
│   └── OrderManagement.Infrastructure.Tests/
│
└── .github/
    └── workflows/
        └── ci-cd.yml

Каждый проект (.csproj) компилируется в отдельную сборку (.dll). Такое разделение обеспечивает физическую изоляцию между слоями -- компилятор не позволит обратиться к классу из слоя, на который нет ссылки.

Правило зависимостей

Ключевой принцип архитектуры -- зависимости направлены внутрь, от внешних слоёв к внутренним:

API → Application → Domain ← Infrastructure

Это означает следующее:

  • Domain не зависит ни от чего. Это чистый C# без внешних библиотек (кроме интерфейсов MediatR.Contracts для доменных событий).
  • Application зависит только от Domain. Знает о сущностях, Value Objects и интерфейсах репозиториев.
  • Infrastructure зависит от Application и Domain. Реализует интерфейсы, определённые во внутренних слоях.
  • API зависит от Application (для отправки команд и запросов) и Infrastructure (только для регистрации зависимостей в DI-контейнере).

Обратите внимание на стрелку: Infrastructure зависит от Domain, а не наоборот. Domain определяет интерфейс IOrderRepository, а Infrastructure его реализует через Entity Framework Core. Это принцип инверсии зависимостей (DIP из SOLID).

Ответственность каждого слоя

Domain Layer -- ядро

Содержит бизнес-правила, сущности, объекты-значения, агрегаты и доменные события. Здесь живёт ответ на вопрос «что делает система с точки зрения бизнеса». Domain Layer не знает ни о базе данных, ни о HTTP, ни об Azure.

Application Layer -- координация

Содержит сценарии использования (Use Cases): команды для изменения данных и запросы для чтения. Использует паттерн CQRS с MediatR. Здесь же располагается валидация входных данных через FluentValidation и Pipeline Behaviors для кросс-функциональных задач (логирование, валидация).

Infrastructure Layer -- техническая реализация

Содержит всё, что связано с внешним миром: Entity Framework Core для работы с базой данных, реализации репозиториев, интеграции с Redis, Service Bus и другими сервисами Azure. Этот слой можно заменить без изменения бизнес-логики.

API Layer -- точка входа

Содержит ASP.NET Core контроллеры, middleware для обработки ошибок, конфигурацию DI-контейнера и документацию API через OpenAPI и Scalar. Контроллеры максимально тонкие -- они только принимают HTTP-запрос, создают Command или Query, отправляют через MediatR и возвращают результат.

Технологический стек

Компонент Технология
Язык C# 14, .NET 10
Web Framework ASP.NET Core Controllers
ORM Entity Framework Core 10
CQRS MediatR
Валидация FluentValidation
API-документация OpenAPI (встроенный) + Scalar
Логирование Serilog + Application Insights
Тестирование xUnit, FluentAssertions, Moq, Testcontainers
БД Azure SQL Database
CI/CD GitHub Actions

Диаграмма компонентов

┌──────────────────────────────────────────────────┐
│                    Клиенты                        │
│              (SPA, Mobile, Other)                 │
└──────────────────┬───────────────────────────────┘
                   │ HTTPS
┌──────────────────▼───────────────────────────────┐
│              API Layer (ASP.NET Core)             │
│  Controllers / Middleware / Scalar                │
└──────────────────┬───────────────────────────────┘
                   │ MediatR
┌──────────────────▼───────────────────────────────┐
│           Application Layer (Use Cases)           │
│  Commands / Queries / Validators / DTOs           │
└──────────────────┬───────────────────────────────┘
                   │ Interfaces
┌──────────────────▼───────────────────────────────┐
│              Domain Layer (Core)                  │
│  Entities / Value Objects / Aggregates / Events   │
└──────────────────────────────────────────────────┘
                   ▲ implements
┌──────────────────┴───────────────────────────────┐
│          Infrastructure Layer                     │
│  EF Core / Repositories / Azure Services          │
└──────────────────────────────────────────────────┘

Запрос клиента проходит следующий путь: HTTP-запрос попадает в контроллер API Layer, контроллер формирует команду и отправляет её через MediatR, MediatR пропускает команду через Pipeline Behaviors (логирование, валидация) и передаёт в Handler Application Layer, Handler использует интерфейсы репозиториев из Domain Layer, а Infrastructure Layer предоставляет реализации этих интерфейсов.

Создание проекта

Для создания всей структуры используются команды .NET CLI:

// Создание Solution
// dotnet new sln -n OrderManagement

// Создание проектов по слоям
// dotnet new classlib -n OrderManagement.Domain
// dotnet new classlib -n OrderManagement.Application
// dotnet new classlib -n OrderManagement.Infrastructure
// dotnet new webapi -n OrderManagement.API

// Установка зависимостей между проектами
// Application -> Domain
// Infrastructure -> Domain + Application
// API -> Application + Infrastructure

Domain и Application создаются как Class Library (.dll), а API -- как Web API (с точкой входа Program.cs).

Преимущества выбранной архитектуры

Разделение на слои даёт конкретные преимущества:

  1. Тестируемость. Domain Layer тестируется без базы данных, HTTP-сервера и внешних зависимостей. Это самые быстрые и надёжные тесты.
  2. Заменяемость. Можно поменять базу данных с SQL Server на PostgreSQL, изменив только Infrastructure Layer.
  3. Независимость команды. Разные разработчики могут работать над разными слоями параллельно.
  4. Понятная структура. Новый разработчик сразу видит, где искать бизнес-логику, а где -- технические детали.

Проверь себя

🧪

Какой слой в Clean Architecture не имеет внешних зависимостей?

🧪

В каком направлении идут зависимости между слоями в Clean Architecture?

🧪

Почему API Layer не ссылается напрямую на Domain Layer?

🧪

Какой тип проекта .NET используется для Domain Layer?