Monoliths, Microservices & Event-Driven Systems
Three ways to organize a backend's internal structure — each solving a different scale of problem.
1.5.1Definitions
A monolith is a backend built and deployed as one single, unified codebase and process — every feature lives in the same application. A microservices architecture instead splits the backend into many small, independently deployable services, each owning one specific responsibility and communicating over the network. An event-driven system organizes communication around asynchronous events — a service announces that something happened, and any number of other services can react, without the announcing service needing to know who's listening. These are not mutually exclusive: many real systems are microservices that communicate through events.
one codebase
1.5.2Why They Exist
A monolith is the natural starting point for almost any product — one codebase is simpler to build, test, deploy, and reason about than several. Microservices exist to solve problems monoliths develop only at real scale: when different parts of a system need to scale independently (checkout traffic vs. reporting traffic), when different teams need to deploy without blocking each other, or when a single failing component shouldn't take down the entire application. Event-driven design exists to decouple services further, so producers and consumers of information don't need direct, synchronous knowledge of each other.
1.5.3When to Use Each
| Pattern | Best fit |
|---|---|
| Monolith | New products, small teams, unproven product-market fit, most startups |
| Microservices | Large teams needing independent deployment, components with very different scaling needs |
| Event-driven | Systems with many loosely related consumers of the same underlying event (order placed, user signed up) |
1.5.4Common Mistakes
- Adopting microservices prematurely. Splitting a product with a small team and low traffic into a dozen services, multiplying operational complexity (deployment, monitoring, networking) long before any of its benefits are actually needed.
- Building a "distributed monolith." Splitting code into separate services that are still tightly coupled and must be deployed together — inheriting all the complexity of microservices with none of the independence benefit.
- Using events for things that need a synchronous, guaranteed-order response (e.g., authorizing a payment before showing a confirmation), where the added latency and complexity of eventual consistency actively hurts the user experience.
1.5.5Best Practices
- Start with a well-organized monolith by default; migrate specific pieces to services only when a concrete, measured need appears.
- If splitting into services, split along genuine business boundaries (billing, inventory, notifications) — not arbitrary technical lines.
- Use events for "something happened, multiple things may care" scenarios; use direct requests for "I need an answer right now" scenarios.