Most systems start here, and many should stay. Understand client/server, the layered style, and why a well-structured monolith beats premature microservices.
Why: the foundational structure is client/server (a client requests, a server responds), and within the server the layered (n-tier) style organizes code into horizontal layers — presentation, business logic, data access — each depending only on the one below. When: use layering as the default internal structure; it separates concerns and is easy to reason about. Where: nearly every application, monolith or service, uses layering internally.
CLIENT/SERVER: client ──request──► server ──response──► client
LAYERED (n-tier) inside the server — each layer depends only DOWNWARD:
Presentation (controllers / API) <- handles requests
│
Business Logic (services / domain) <- the rules
│
Data Access (repositories) <- talks to the DB
│
Database
Simple, separable, and the internal structure of most systems.Why: a monolith deploys the whole application as one unit, and — despite its bad reputation — a well-structured (modular) monolith is simpler to build, test, deploy, and debug, making it the right default for most systems and nearly all early-stage ones. When: start with a monolith; you get one deployable, in-process calls (no network), and simple transactions. Where: premature microservices are one of the most common and costly architecture mistakes.
MONOLITH: one deployable unit. Modular inside, single process outside.
+ simple to build, test, deploy, debug
+ in-process calls (fast, no network failures)
+ easy transactions + strong consistency
- scales as ONE unit; a big team contends on one codebase/deploy
Default to a well-MODULARIZED monolith. Split it only when a real force
(scale, team autonomy) demands it — not because microservices are trendy.Why: the way to keep the option of splitting later is to give the monolith clear internal module boundaries now (by business capability, with explicit interfaces) so a module can be extracted into a service when a real need arises — without a rewrite. When: enforce module boundaries from day one; the hard part of microservices is the boundaries, and a modular monolith lets you find them cheaply. Where: this "modular monolith first" path is the low-risk route to microservices if you ever need them.
A MODULAR MONOLITH — one deploy, but clean internal seams:
[ Orders module ] [ Billing module ] [ Inventory module ]
│ │ │
└──── call each other only through INTERFACES ────┘
(no reaching into another module's tables)
Get the BOUNDARIES right in-process (cheap to move). If a module later
needs its own scaling/team, extract it into a service — no rewrite.