Logo Craft Homelab Docs Контакты Telegram

Эпилог (стр. 268-289)

И что теперь?

Главная мысль: эта книга — не руководство по созданию идеальных систем, а набор кубиков для построения вашего первого дома.

Как мне добраться туда? (Рефакторинг легаси)

Проблема: «У меня большой комок грязи Django, как перейти к этой красивой архитектуре?»

Шаг 1: Определите цель

  • Трудно вносить изменения?
  • Проблемы с производительностью?
  • Странные баги?

«Налог на архитектуру»: выделите время на рефакторинг при запуске нового функционала.

Разделение запутанных обязанностей

Шаг 1: Создайте сервисный слой

Было:
┌─────────────────────────────────────┐
│  Views → Managers → Models → DB     │
│  ↑________↓_________________↑       │
│        (хаос, циклы)                │
└─────────────────────────────────────┘

Стало:
┌─────────────────────────────────────┐
│  Views → Use Cases → Models → DB    │
│           (один вход)               │
└─────────────────────────────────────┘

Каждый вариант использования:

  1. Запускает свою транзакцию
  2. Достает данные
  3. Проверяет условия
  4. Обновляет модель
  5. Сохраняет изменения

Совет: лучше дублирование кода, чем цепочки вызовов между 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 этапа:

  1. Вызов событий в старой системе
  2. Новая система потребляет события
  3. Замена старой системы

Пример из MADE.com:

БЫЛО:
┌──────────────┐    XML-RPC    ┌──────────────┐
│   Фронтенд   │◀─────────────▶│   Бэкенд     │
│  (магазин)   │   24 часа!    │ (исполнение) │
└──────────────┘               └──────────────┘

СТАЛО:
┌──────────────┐    События    ┌──────────────┐
│   Фронтенд   │──────────────▶│  Служба      │
│              │◀──────────────│  наличия     │
└──────────────┘   2-3 мс!     └──────────────┘

Результат: сотни запросов в секунду вместо 24 часов на импорт!

Как убедить стейкхолдеров попробовать что-то новое?

Совет: используйте событийный штурм (Event Storming)

  • Инженеры + владельцы продукта + клиенты
  • Один язык для обсуждения
  • Выявление сложных частей системы

Вопросы научных редакторов

Q: Должен ли я сразу всё применять?

A: Нет! Начинайте постепенно:

  1. Сервисный слой
  2. Модель предметной области
  3. События

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 — легкий путь

Книги для обязательного прочтения

  1. Eric Evans — Domain-Driven Design (Синяя книга)
  2. Martin Fowler — Patterns of Enterprise Application Architecture
  3. Vaughn Vernon — Implementing Domain-Driven Design (Красная книга)
  4. 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 главных выводов:

  1. Модель предметной области — код на языке бизнеса
  2. Репозиторий — абстракция над хранением
  3. UoW — атомарные транзакции
  4. Агрегаты — границы согласованности
  5. События — факты произошедшего
  6. Команды — намерения (обязательны к выполнению)
  7. Шина сообщений — оркестровка через события
  8. CQRS — разделение чтения и записи
  9. DI + Bootstrap — явные зависимости
  10. TDD — тесты на всех уровнях