Структура решения¶
В практике используется единое решение ActivityMonitoring. Оно разделено на четыре слоя.
Схема ниже нужна как карта проекта: по ней видно, в каком проекте должен лежать каждый тип файла и почему контроллеры, сущности и работа с базой не смешиваются в одной папке.
ActivityMonitoring/
ActivityMonitoring.Api/ # Presentation: контроллеры, JWT, DI, Program.cs
ActivityMonitoring.Application/ # сервисы, DTO, интерфейсы, аналитика
ActivityMonitoring.Domain/ # сущности, enum, доменные правила
ActivityMonitoring.Infrastructure/ # EF Core, репозитории, отчёты, генератор событий
Назначение слоёв¶
| Слой | За что отвечает | Что не должен делать |
|---|---|---|
Domain |
Сущности и правила предметной области | Не работает с HTTP, EF Core и файлами |
Application |
Сценарии использования, DTO, сервисы | Не знает конкретную СУБД |
Infrastructure |
База данных, репозитории, отчёты, внешние реализации | Не содержит HTTP-контроллеры |
Api |
REST API, авторизация, маршруты, Swagger | Не хранит бизнес-логику в контроллерах |
Зависимости проектов¶
Этот блок показывает разрешённые направления ссылок между проектами. Зависимости идут от внешних слоёв к внутренним: API может знать о сервисах, а доменная модель не должна знать ни о вебе, ни о базе данных.
Api -> Application
Api -> Infrastructure
Infrastructure -> Application
Infrastructure -> Domain
Application -> Domain
Domain -> ни от кого не зависит
Такая схема позволяет заменить SQLite на PostgreSQL или MS SQL без переписывания контроллеров и бизнес-логики.
Основные сущности¶
Диаграмма фиксирует минимальную предметную модель. Она помогает заранее понять, почему событие связано с пользователем, а пользователь — с ролью, и какие таблицы появятся после миграции.
erDiagram
APP_USER ||--o{ ACTIVITY_EVENT : creates
ROLE ||--o{ APP_USER : assigned
ROLE {
int Id
string Name
}
APP_USER {
int Id
string UserName
string PasswordHash
int RoleId
bool IsActive
}
ACTIVITY_EVENT {
int Id
int UserId
string Type
string Source
string Description
string Severity
datetime CreatedAt
}
Поток выполнения запроса¶
Последовательность ниже объясняет, зачем в проекте нужны все слои сразу. Один HTTP-запрос проходит через контроллер, сервис, репозиторий и DbContext, а каждый участник выполняет только свою часть работы.
sequenceDiagram
participant Client as Клиент
participant Api as EventsController
participant Service as ActivityEventService
participant Repo as Repository
participant Db as EF Core DbContext
Client->>Api: POST /api/events
Api->>Service: CreateAsync(dto, userId)
Service->>Service: Валидация и бизнес-правила
Service->>Repo: AddAsync(entity)
Repo->>Db: SaveChangesAsync()
Db-->>Repo: Id события
Repo-->>Service: entity
Service-->>Api: EventDto
Api-->>Client: 201 Created