Architecture Styles — Deep Dive

Level: Beginner → Intermediate
Pre-reading: 01 · Architectural Foundations


The Evolution of Architecture Styles

Software architecture has evolved in response to changing business needs, team structures, and technology capabilities. Understanding this evolution helps you make informed choices about where your system sits on the spectrum.

graph LR
    A[Monolith] --> B[Modular Monolith]
    B --> C[SOA]
    C --> D[Microservices]
    D --> E[Serverless]
    D --> F[Event-Driven]
    E --> G[Edge Computing]

Monolith — The Starting Point

A monolith is a single deployable unit containing all application logic, typically sharing one database.

Aspect Characteristics
Deployment One artifact (WAR, JAR, binary)
Database Single shared schema
Scaling Vertical or horizontal (whole app)
Team structure One team or tightly coordinated teams

When Monolith Is the Right Choice

  • Early-stage startups — speed to market matters more than scalability
  • Small teams (< 10 developers) — coordination overhead of microservices isn't justified
  • Unclear domain boundaries — you don't know where to split yet
  • Simple CRUD apps — no complex business logic warranting separation

Start with a monolith

Martin Fowler's "Monolith First" advice: it's much easier to extract services from a well-structured monolith than to merge poorly-designed microservices back together.

When to Move Away from Monolith

  • Deployments are slow and risky (everything deploys together)
  • Teams step on each other's code constantly
  • Scaling is inefficient (scale the whole app for one hot path)
  • Technology lock-in (can't adopt new tech for one component)

Modular Monolith — The Stepping Stone

A modular monolith maintains a single deployable unit but enforces strict internal boundaries between modules. Each module owns its data and communicates via well-defined interfaces.

graph TD
    subgraph Modular Monolith
        A[Order Module] -->|Interface| B[Payment Module]
        B -->|Interface| C[Inventory Module]
        A -->|Interface| C
    end
    subgraph Single DB
        D[(Orders Schema)]
        E[(Payments Schema)]
        F[(Inventory Schema)]
    end
    A --> D
    B --> E
    C --> F
Benefit Description
Clear boundaries Modules can evolve independently
Single deploy Operational simplicity retained
Refactoring safety Interfaces enforce contracts
Extraction path Each module is a candidate microservice

Tools: Spring Modulith, Java 9+ modules (JPMS), .NET projects/assemblies

Discipline required

Without enforcement, developers will bypass module interfaces. Use ArchUnit or similar tools to enforce architectural rules at build time.


Service-Oriented Architecture (SOA)

SOA emerged in the early 2000s as an enterprise integration pattern. Services communicate via an Enterprise Service Bus (ESB) — a centralized middleware handling routing, transformation, and orchestration.

Aspect SOA Characteristic
Services Coarse-grained, often aligned with enterprise capabilities
Communication ESB-mediated; SOAP/XML common
Governance Centralized; schema and contract management
Coupling ESB is a central point of coupling and failure

SOA vs Microservices

SOA Microservices
Service size Large, enterprise-wide Small, single responsibility
Communication ESB, SOAP Direct, REST/gRPC/async
Data Often shared databases Database per service
Governance Centralized Decentralized
Deployment Often coordinated Independent

SOA isn't dead

Many enterprises still run SOA. Microservices can be seen as SOA done right — without the ESB bottleneck and with true service independence.


Microservices — Independent Services at Scale

Microservices are small, independently deployable services, each owning its data and business capability.

Principle Implementation
Single responsibility One service = one bounded context
Independent deployment Change and deploy without coordinating with others
Decentralized data Each service owns its database
Smart endpoints, dumb pipes Business logic in services, not middleware
Design for failure Circuit breakers, retries, graceful degradation

Microservices Trade-offs

Benefit Cost
Independent scaling Distributed system complexity
Team autonomy Service coordination overhead
Technology flexibility Operational burden (many deployables)
Fault isolation Network reliability challenges
Faster deployments Data consistency complexity

Prerequisites for Microservices Success

  • [ ] Mature CI/CD pipeline with automated testing
  • [ ] Container orchestration (Kubernetes)
  • [ ] Observability stack (logs, metrics, traces)
  • [ ] Service discovery and load balancing
  • [ ] Clear domain boundaries (DDD)
  • [ ] Team structure aligned with services

Serverless / FaaS — Functions as a Service

Serverless abstracts infrastructure entirely. You deploy functions; the cloud provider handles scaling, availability, and billing (per invocation).

Concept Description
Function Single-purpose code triggered by events
Cold start Latency penalty when function instance spins up
Stateless No local state between invocations
Event-driven Triggered by HTTP, queue messages, schedules, etc.

Serverless Trade-offs

Benefit Cost
Zero infrastructure management Cold start latency
Auto-scaling to zero Vendor lock-in
Pay per invocation Execution time limits
Rapid development Debugging complexity

When to Choose Serverless

  • Bursty, unpredictable workloads — pay only for what you use
  • Event processing — file uploads, webhooks, queue consumers
  • APIs with low/moderate traffic — cost-effective for small scale
  • Scheduled jobs — cron-like tasks without managing servers

When NOT to Choose Serverless

  • Low-latency requirements — cold starts are unpredictable
  • Long-running processes — execution limits (15 min on AWS Lambda)
  • Stateful workloads — functions are ephemeral
  • High-volume, consistent traffic — containers may be cheaper

Event-Driven Architecture — Events as First-Class Citizens

Event-driven architecture uses asynchronous events as the primary integration mechanism. Producers emit events; consumers react independently.

graph LR
    P1[Order Service] -->|OrderPlaced| EB[Event Bus]
    P2[Payment Service] -->|PaymentCompleted| EB
    EB --> C1[Inventory Service]
    EB --> C2[Notification Service]
    EB --> C3[Analytics Service]
Pattern Description
Event Notification Notify that something happened; consumer queries for details
Event-Carried State Transfer Event contains full payload; no follow-up query needed
Event Sourcing Events are the source of truth; state derived by replay

Deep Dive: Event-Driven Architecture for patterns, Kafka, CQRS, and Saga


Choosing an Architecture Style — Decision Framework

graph TD
    A[New Project] --> B{Team size < 10?}
    B -->|Yes| C{Domain understood?}
    C -->|No| D[Monolith]
    C -->|Yes| E[Modular Monolith]
    B -->|No| F{Clear bounded contexts?}
    F -->|No| E
    F -->|Yes| G{Ops maturity high?}
    G -->|No| E
    G -->|Yes| H[Microservices]
    H --> I{Bursty workloads?}
    I -->|Yes| J[Consider Serverless for some services]
Factor Monolith Modular Monolith Microservices Serverless
Team size 1–10 5–30 20+ Any
Domain clarity Low Medium High High
Ops maturity Low Low High Medium
Latency needs Any Any Any Tolerant
Release frequency Weekly Weekly Daily+ Instant

When would you recommend a modular monolith over microservices?

When the team lacks mature DevOps practices (CI/CD, K8s, observability), when domain boundaries are still being discovered, or when the team is small (< 20 developers). A modular monolith gives you clear boundaries and an extraction path without the operational overhead of distributed systems.

What is the main difference between SOA and microservices?

SOA uses centralized governance with an ESB for routing and transformation — the ESB becomes a coupling point. Microservices use decentralized governance with smart endpoints (services) and dumb pipes (HTTP, messaging) — no central bottleneck. Microservices also enforce database-per-service, while SOA often shared databases.

What are the hidden costs of microservices that teams often underestimate?

(1) Operational complexity — managing dozens/hundreds of deployables, (2) Distributed debugging — tracing requests across services, (3) Data consistency — eventual consistency and saga complexity, (4) Network reliability — handling partial failures, timeouts, retries, (5) Testing — integration and contract testing across service boundaries.