Общие паттерны проектирования backend-сервисов AIOps.
Clean Architecture: слои
Core (Domain Layer)
- Содержит: сущности (entities), value objects, протоколы репозиториев, domain services, domain errors.
- Зависимости: только стандартная библиотека и Pydantic. Никаких импортов из
infra,api, БД, Kafka. - Пример:
User,Identifier,VerificationChallengeв Identity service;PasswordCredentialв Credential service.
Use Cases (Application Layer)
- Содержит: сценарии применения (CreateUserUseCase, VerifyPasswordUseCase и т.д.).
- Зависимости: только
core. Вызывает репозитории и domain services через абстракции (протоколы). - Ответственность: оркестрация шагов, валидация входных данных, вызов domain logic.
- Путь:
use_cases/
Infrastructure Layer
- Содержит: реализация репозиториев (PostgreSQL), Unit of Work, Kafka publisher, внешние клиенты.
- Зависимости:
core,use_cases, а также SQLAlchemy, aiokafka, redis и т.д. - Пример:
PostgresUserRepository,OutboxEventRepository,KafkaEventPublisher.
API Layer (Presentation)
- Содержит: gRPC servicers, маппинг request/response ↔ DTO, обработка ошибок.
- Зависимости:
use_cases,core. Не содержит бизнес-логики.
Unit of Work
Все изменения данных в рамках одного сценария выполняются в одной транзакции.
1 2 3 4 5 6 7 8 9 | |
Использование в use case:
1 2 3 4 5 6 | |
Транзакция коммитится при выходе из контекста; при исключении — rollback.
Repository Pattern
- Протокол объявляется в
core/<aggregate>/repositories.pyи возвращает domain-сущности. - Реализация в
infra/repositories/работает с ORM и БД.
Правила: - Репозиторий не возвращает ORM-модели наружу. - Сложные запросы (фильтры, пагинация) инкапсулируются в методах репозитория. - Агрегат — граница консистентности; репозиторий обычно один на агрегат.
Domain Services
Логика, которая не принадлежит одной сущности: - UserDomainService (Identity) — валидация username, enable/disable пользователя. - PasswordDomainService (Credential) — хеширование Argon2id, проверка сложности через zxcvbn.
Они вызываются из use case и получают зависимости через DI (например, репозитории).
Dependency Injection (Dishka)
- Контейнер собирается в точке входа (api или worker).
- Провайдеры из
dishka-providersрегистрируют DB session, Redis, Kafka, UoW, репозитории. - Use cases и servicers получают UoW и прочие зависимости через конструктор.
Транзакционность и события
События публикуются в той же транзакции, что и бизнес-данные (Transactional Outbox): запись в таблицу outbox_events, затем отдельный worker читает outbox и шлет сообщения в Kafka. Подробнее: Event-Driven Patterns.
Обработка ошибок
- Domain-ошибки (например,
UserAlreadyExists) определяются вcore/errors.py. - В gRPC они маппятся на коды и сообщения (например,
ALREADY_EXISTS). - Неожиданные исключения логируются, не пробрасывают детали наружу; клиент получает общий
INTERNAL.
Подробнее: Error Handling.
Связанные страницы
- Backend Overview — обзор стека
- Database Access Patterns — Repository, UoW
- Event-Driven Patterns — Outbox, Kafka
- Identity service — пример слоев и паттернов