Монолит, модульный монолит или микросервисы: как выбрать в .NET
Классический монолит собирается в одну единицу развёртывания, все домены лежат в одном решении, а данные читаются и пишутся в общую схему базы без ограничений на запросы между доменами. Модульный монолит тоже запускается одним процессом и обычно работает с одним инстансом базы, но внутри разделён на независимые бизнес-модули.
В модульном монолите модули не обращаются к внутренним классам друг друга напрямую: наружу выставляются только публичные контракты, и каждый модуль владеет своими данными, чаще всего через отдельные схемы в базе. Прямые запросы между таблицами разных модулей запрещены на уровне архитектуры. Микросервисы, в свою очередь, деплоятся отдельно и держат закрытую базу у каждого сервиса.
Операционная нагрузка растёт по шкале от монолита к микросервисам. Монолиту достаточно одного CI/CD-пайплайна и локального запуска, модульному монолиту — один контейнер, но с контролем порядка внутри кода на этапе компиляции. Микросервисам нужны оркестрация контейнеров, API-шлюзы, Service Discovery, централизованный сбор логов и распределённая трассировка.
Границы в классическом монолите легко размываются под сроками, а модульный монолит защищает их правилами компиляции и архитектурными тестами вроде NetArchTest. В микросервисах изоляцию обеспечивают процессы ОС и сетевые правила. Общая транзакция базы (ACID) работает в обоих монолитах, тогда как между микросервисами приходится переходить на согласованность в конечном счёте, события интеграции, паттерн Transactional Outbox и распределённые саги.
Набор библиотек зависит от подхода. Монолиту хватает EF Core или Dapper, стандартного контейнера внедрения зависимостей и FluentValidation. Модульному монолиту дополнительно нужны NetArchTest.Rules для контроля границ, MediatR или Wolverine для общения между модулями внутри процесса, EF Core с отдельным DbContext на модуль и Hangfire или Quartz.NET для фоновых задач.
Микросервисам требуется целый инфраструктурный стек: MassTransit поверх RabbitMQ, Apache Kafka или Redis, интеграция Transactional Outbox с EF Core, YARP как реверс-прокси, Polly или Microsoft.Extensions.Resilience для повторов и размыкателей цепи, OpenTelemetry для сквозной трассировки и gRPC для синхронных вызовов. Автор предостерегает от общей базовой библиотеки с бизнес-логикой: любое изменение в ней потребует пересобрать и задеплоить все сервисы.
Классический монолит подходит небольшим проектам и командам из 1–4 разработчиков без жёстко изолированных доменов. Модульный монолит выбирают при росте домена и потребности в чистых границах без Kubernetes и сетевых сбоев. Микросервисы оправданы при нескольких независимых командах со своим графиком релизов, радикально разной нагрузке частей системы и зрелой DevOps-культуре.