Эпилог (стр. 268-289)
И что теперь?
Главная мысль: эта книга — не руководство по созданию идеальных систем, а набор кубиков для построения вашего первого дома.
Как мне добраться туда? (Рефакторинг легаси)
Проблема: «У меня большой комок грязи Django, как перейти к этой красивой архитектуре?»
Шаг 1: Определите цель
- Трудно вносить изменения?
- Проблемы с производительностью?
- Странные баги?
«Налог на архитектуру»: выделите время на рефакторинг при запуске нового функционала.
Разделение запутанных обязанностей
Шаг 1: Создайте сервисный слой
Было:
┌─────────────────────────────────────┐
│ Views → Managers → Models → DB │
│ ↑________↓_________________↑ │
│ (хаос, циклы) │
└─────────────────────────────────────┘
Стало:
┌─────────────────────────────────────┐
│ Views → Use Cases → Models → DB │
│ (один вход) │
└─────────────────────────────────────┘
Каждый вариант использования:
- Запускает свою транзакцию
- Достает данные
- Проверяет условия
- Обновляет модель
- Сохраняет изменения
Совет: лучше дублирование кода, чем цепочки вызовов между use cases!
Определение агрегатов и ограниченных контекстов
Проблема: граф объектов слишком связан
До:
user.account.workspaces[0].documents.versions[1].owner.account.settings[0]
После:
# Заменяем прямые ссылки на ID
class Document:
workspace_id: int # ← Вместо workspace: Workspace
parent_folder_id: int # ← Вместо parent: Folder
Правило: один вариант использования → один агрегат за раз
Если нужно несколько агрегатов:
# Было (плохо)
for workspace in user.account.workspaces:
workspace.archive()
# Стало (хорошо)
workspace_ids = db.query('SELECT id FROM workspace WHERE account_id = ?', user.account_id)
for ws_id in workspace_ids:
bus.handle(LockWorkspace(ws_id)) # ← События!
Подход на основе событий для перехода к микросервисам (Паттерн «Душитель»)
3 этапа:
- Вызов событий в старой системе
- Новая система потребляет события
- Замена старой системы
Пример из MADE.com:
БЫЛО:
┌──────────────┐ XML-RPC ┌──────────────┐
│ Фронтенд │◀─────────────▶│ Бэкенд │
│ (магазин) │ 24 часа! │ (исполнение) │
└──────────────┘ └──────────────┘
СТАЛО:
┌──────────────┐ События ┌──────────────┐
│ Фронтенд │──────────────▶│ Служба │
│ │◀──────────────│ наличия │
└──────────────┘ 2-3 мс! └──────────────┘
Результат: сотни запросов в секунду вместо 24 часов на импорт!
Как убедить стейкхолдеров попробовать что-то новое?
Совет: используйте событийный штурм (Event Storming)
- Инженеры + владельцы продукта + клиенты
- Один язык для обсуждения
- Выявление сложных частей системы
Вопросы научных редакторов
Q: Должен ли я сразу всё применять?
A: Нет! Начинайте постепенно:
- Сервисный слой
- Модель предметной области
- События
Q: Нужно ли разделять CQRS?
A: Не обязательно! Используйте репозитории, если работает.
Q: Можно ли вызывать один use case из другого?
A: Можно, но лучше использовать шину сообщений.
Q: Много агрегатов — это код «с душком»?
A: Да, если нужно атомарно обновлять несколько. Используйте события!
Q: Можно ли использовать с Django?
A: Да! Читайте Приложение Г.
Выстрел в ногу (Предостережения)
⚠️ Надежный обмен сообщениями — это сложно
- Redis Pub/Sub ненадежен (для примеров OK)
- Для продакшена: Kafka, RabbitMQ, Event Store
⚠️ Мы не затрагиваем идемпотентность
# Обработчик должен быть идемпотентным!
def handle(event):
if already_processed(event.id):
return # ← Повторный вызов не меняет состояние
process(event)
mark_as_processed(event.id)
⚠️ События эволюционируют
- Нужно версионирование схем
- JSON Schema + Markdown — легкий путь
Книги для обязательного прочтения
- Eric Evans — Domain-Driven Design (Синяя книга)
- Martin Fowler — Patterns of Enterprise Application Architecture
- Vaughn Vernon — Implementing Domain-Driven Design (Красная книга)
- Michael Feathers — Working Effectively with Legacy Code
Приложения (краткий обзор)
Приложение А: Сводная диаграмма и таблица
Все паттерны в одной таблице:
- Репозиторий
- UoW
- События
- Команды
- CQRS
Приложение Б: Шаблонная структура проекта
1 project/
2 ├── domain/
3 │ ├── model.py
4 │ ├── events.py
5 │ └── commands.py
6 ├── service_layer/
7 │ ├── handlers.py
8 │ ├── messagebus.py
9 │ ├── unit_of_work.py
10 │ └── bootstrap.py
11 ├── adapters/
12 │ ├── repository.py
13 │ ├── orm.py
14 │ └── redis_eventpublisher.py
15 ├── entrypoints/
16 │ ├── flask_app.py
17 │ └── redis_eventconsumer.py
18 └── tests/
19 ├── unit/
20 ├── integration/
21 └── e2e/
Приложение В: Замена инфраструктуры (CSV вместо БД)
Показывает, как легко заменить БД на CSV благодаря абстракциям!
Приложение Г: Репозиторий и UoW с Django
Специально для Django-разработчиков!
Приложение Д: Валидация
Паттерн «Обеспечение» (Specification Pattern) для проверок.
ИТОГИ КУРСА
Что мы прошли:
┌──────────┬───────┬───────────────────────────────────────────────────────┐
│ Часть │ Главы │ Ключевые паттерны │
├──────────┼───────┼───────────────────────────────────────────────────────┤
│ Часть I │ 1-7 │ Модель предметной области, Репозиторий, UoW, Агрегаты │
│ Часть II │ 8-13 │ События, Шина сообщений, Команды, CQRS, DI │
└──────────┴───────┴───────────────────────────────────────────────────────┘
10 главных выводов:
- Модель предметной области — код на языке бизнеса
- Репозиторий — абстракция над хранением
- UoW — атомарные транзакции
- Агрегаты — границы согласованности
- События — факты произошедшего
- Команды — намерения (обязательны к выполнению)
- Шина сообщений — оркестровка через события
- CQRS — разделение чтения и записи
- DI + Bootstrap — явные зависимости
- TDD — тесты на всех уровнях
