Web application architecture: from a clear monolith to scalable systems
Architecture starts with boundaries#
Architecture is not about showing complexity. It is about managing change. A well-structured monolith is often more effective than premature microservices when the team, domain, and operations are still evolving.
A monolith is not the enemy#
Monoliths are easy to deploy, debug, and transact across. The problem is not one deployable unit; the problem is unclear boundaries. A modular monolith keeps one deployment while separating domains such as identity, content, billing, notification, and reporting.
Practical layering#
- Route/controller: parse requests and return responses.
- Application service: orchestrate use cases, transactions, permissions, and events.
- Domain logic: hold core rules such as publishing, state transitions, and eligibility checks.
- Data access: query and map database data.
- Adapters: integrate email, payments, storage, and external APIs.
Database decisions matter#
For most web apps, the database is the most valuable system. Use constraints for important invariants: unique slugs, foreign keys, valid status values, non-negative counters, and required fields. App validation improves UX; database constraints protect correctness.
Cache and queues#
Cache can reduce latency and database load, but it adds stale-data risk. Define TTL, cache keys, and invalidation before adding it. Queue slow or retryable work such as email, image processing, webhook delivery, report generation, and external sync. Design jobs to be idempotent.
When to split services#
Split a service when domain ownership is clear, contracts are stable, scaling needs differ, and the team can operate the extra complexity. Splitting a messy monolith too early often creates multiple messy services plus network failure modes.
Architecture checklist#
- Modules are named by domain, not only by technical layer.
- Controllers stay thin and business rules live in services or domain code.
- Database constraints protect important data rules.
- Indexes match real product queries.
- Background jobs handle slow or retryable work.
- Cache has clear TTL, keys, and invalidation.
- Logs and metrics map to important user-facing use cases.
Primary references#
Share this article
Help others discover this article
Comments
Loading comments...
Loading comments...