Модульный монолит

↑ 8.2 Архитектурные стили и DDD · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

Часто правильный ответ на «микросервисы или монолит»: модульный монолит с границами, готовыми к выделению.

Объяснение

Один процесс и деплой, но код разделён на модули с чёткими границами, которые соответствуют bounded contexts.

СвойствоТребование
Границымодуль = отдельные проекты/сборки, публичный контракт (интерфейсы/события), всё остальное internal
Данныеу каждого модуля своя схема/таблицы; нет JOIN между модулями и общих таблиц
Взаимодействиечерез публичные API модулей или доменные события (in-process bus)
Зависимостибез циклов; проверяются архитектурными тестами
Командымодуль принадлежит команде
Эволюциямодуль можно вынести в сервис, заменив вызовы на сетевые
src/
  Modules/
    Orders/       Orders.Contracts  Orders.Core  Orders.Infrastructure
    Billing/      Billing.Contracts Billing.Core Billing.Infrastructure
    Catalog/      ...
  Host/           # композиция модулей, единый процесс
public interface IBillingModule { Task<InvoiceId> CreateInvoiceAsync(OrderId id, Money total, CancellationToken ct); }   // публичный контракт
builder.Services.AddOrdersModule(cfg).AddBillingModule(cfg);

Плюсы перед микросервисами: простота эксплуатации и транзакций, ниже задержка, легче рефакторинг. Плюсы перед «большим комом»: границы и независимость.

Нюансы и подводные камни

  • Без дисциплины (общая БД, прямые ссылки на внутренние типы) модули сливаются обратно в ком.
  • Общий код Common разрастается в новый монолит внутри монолита.
  • Транзакции между модулями появляются «случайно»: используйте события и eventual consistency, если планируете вынос.
  • Один релиз для всех модулей: тесты и зависимость релизов.

Практика

  1. Разделите проект на 2–3 модуля с контрактами и internal реализацией.
  2. Добавьте архитектурный тест на запрет ссылок на внутренности модулей.
  3. Разведите схемы БД по модулям.

Вопросы с ответами

Связанные темы