System Design Cases
Modular Monolith
Modular Monolith concept page: one deploy unit with hard internal module boundaries (catalog/orders/payments), own DB schemas per module, in-process event bus, dependency-cruiser as fitness function, Strangler Fig extraction to microservice when operationally justified.
Modular monolith: enforced boundaries inside one deployable
Modular monolith — один deployable/runtime boundary с внутренними modules, которые имеют явные APIs, ownership и dependency rules. Он может упростить deployment и local transactions, но не гарантирует 100K RPS, error isolation или лёгкое будущее разделение. Modularity надо проверять кодом и данными, иначе monolith остаётся tightly coupled.
Что утверждает паттерн — и чего он не гарантирует
- A modular monolith is one deployment with intentionally enforced internal boundaries; folder names alone are insufficient.
- Local calls avoid network failure modes but share process, resource and deployment failure domains.
- In-memory events do not guarantee durability or handler isolation; durable asynchronous delivery needs storage, retry and idempotency.
- Fowler presents monolith-first and extraction as contextual strategies with sparse evidence, not a universal success guarantee.
Границы и компоненты
| Компонент | Ответственность |
|---|---|
| Single Deployment Entrypoint | Маршрутизирует requests к public module APIs в одном process. |
| Orders Module | Владеет Orders API, model, schema и transaction rules. |
| Billing Module | Владеет Billing API, model и idempotent operations. |
| Catalog Module | Предоставляет read API без table-level shortcuts. |
| Logically Partitioned Database | Один engine допустим, но tables/schema ownership разделены. |
| Module Transactional Outbox | Коммитит durable integration intent вместе с module state. |
| Outbox Relay | Повторяет event delivery и изолирует handler failure от producer commit. |
| Candidate Extracted Service | Shadow/consume path с idempotency и explicit ownership epoch. |
| Architecture Boundary Tests | Запрещает invalid imports и cross-module table access. |
Сценарии
Call through a module API
The entrypoint invokes Orders. Orders reads Catalog and asks Billing only through declared APIs; no module imports another module’s internals or tables.
Проверяемый исход: A boundary test can identify every allowed dependency and owner.
Choose a transaction boundary
A local operation can use one DB transaction only when ownership and coupling are intentionally accepted. Cross-module invariants require an explicit owner/API rather than ad hoc multi-table writes.
Проверяемый исход: Transaction convenience does not erase module ownership or create a distributed guarantee after extraction.
Deliver a durable module event
Orders commits an outbox row. Relay retries after crashes; a failing downstream handler cannot roll back the already committed order and must deduplicate.
Проверяемый исход: In-process location is not confused with durable delivery or error isolation.
Extract a module with an ownership epoch
Backfill and shadow reads validate candidate state. Old and new paths use stable operation IDs; a fenced routing epoch selects one authoritative writer before contract removal.
Проверяемый исход: There is no unfenced dual-writer interval and rollback remains compatible.
Failure, concurrency и evolution checklist
- Fail CI on forbidden module imports and cross-owner table queries.
- Set time/CPU/allocation budgets for local handlers so one module cannot monopolize the process.
- Use outbox plus idempotent consumer for durable module integration.
- Extract with backfill, shadow validation, fencing, versioned contracts and rollback.
Метрики, units и допущения
- Throughput is measured under workload and shared-resource contention; one process has no fixed RPS ceiling.
- Module budget includes CPU ms/request, allocations/request, DB connections and queue lag with units.
- Extraction lag = produced changes/s − applied changes/s; cutover requires non-positive sustainable lag and a declared threshold.
Числа здесь — размерностные формулы или явно помеченные учебные inputs. Паттерн архитектуры сам по себе не задаёт SLA, throughput, latency или fault tolerance.
Связанные темы
[CONCEPT]evolutionary-architecture
Первичные источники
- https://martinfowler.com/bliki/MonolithFirst.html
- https://martinfowler.com/articles/microservice-trade-offs.html
Scope note
The lesson models one safe modular-monolith operating approach. It does not claim a monolith is always preferable, automatically modular, fault-isolated, horizontally scalable or trivially extractable.