Learning Material

Software Architecture Patterns Explained

Learning Material · Software Engineering

Cover: Software Architecture Patterns Explained

The monolith: one deployable unit

A monolith is a single application containing all of a system's functionality, deployed as one unit. It's often maligned, but for most teams and most products, it's the right starting point: one codebase to understand, one thing to deploy, no network calls between internal components, and refactoring across the whole system is a single, simple change.

Microservices: independent, deployable pieces

A microservices architecture splits a system into small, independently deployable services, each owning a specific piece of business functionality (an orders service, a payments service, a notifications service), communicating over the network.

Order Service  --HTTP-->  Payment Service
      |
      +--------HTTP-->  Notification Service

The appeal is real: teams can deploy independently, scale only the service that needs it, and use different tech per service if warranted. The cost is also real: network calls replace function calls (slower, and can fail in new ways), debugging a request across five services is harder than reading one stack trace, and you now need infrastructure — service discovery, distributed tracing — that a monolith never required.

When microservices actually pay off

They tend to make sense once a single team can no longer own the whole codebase, once different parts of the system have genuinely different scaling needs, or once independent deployability is worth more to the organization than the added operational complexity. Adopting microservices before you have that pain is one of the most common architecture mistakes — it front-loads real complexity for a problem you don't have yet.

Event-driven architecture: reacting instead of asking

OrderPlaced event
   ├── Inventory service decrements stock
   ├── Email service sends confirmation
   └── Analytics service logs the sale

Instead of one service directly calling three others, it publishes an event, and any interested service reacts independently. This decouples the publisher from needing to know who's listening — you can add a new subscriber (a new analytics pipeline, say) without touching the code that publishes the event at all.

These aren't mutually exclusive. A "modular monolith" — one deployment, but with clean internal boundaries between modules — often captures most of microservices' organizational benefits (clear ownership, low coupling) without the operational cost, and is a common, sensible middle ground.

The question that actually matters

Architecture decisions should follow from actual organizational and scaling constraints, not from what's currently fashionable. The best architecture is the simplest one that solves the problem you actually have — added complexity should be a deliberate trade for a specific, real benefit, not a default.

Comments