Course → Module 1: Architectural Foundations & Core Concepts

Patterns as Reusable Decisions

An architectural pattern is a proven structural arrangement for organizing a software system. It is not a library or a framework. It is a set of decisions about how components are arranged, how they communicate, and where responsibilities live. Choosing a pattern means choosing which tradeoffs you accept.

This session surveys five major patterns. Each one solves a different class of problem. Most real production systems combine two or more of them.

Multi-Tier Architecture

Multi-tier (often called n-tier) separates the system into horizontal layers, each handling a distinct concern. The classic version has three tiers: presentation, business logic, and data. Each tier communicates only with its immediate neighbor.

Multi-tier architecture organizes a system into horizontal layers of responsibility. Each layer provides services to the layer above it and consumes services from the layer below it. Communication between non-adjacent layers is prohibited.

This pattern works because separation of concerns reduces cognitive load. Frontend developers work in the presentation tier without touching database queries. Backend developers implement business rules without worrying about HTML rendering. Database administrators optimize storage without breaking application logic.

The limitation is rigidity. Every request must traverse all tiers, even when only one tier does meaningful work. Adding a new feature often requires changes in every layer. And horizontal scaling is uneven: the data tier typically becomes the bottleneck while the presentation tier sits idle.

Event-Driven Architecture

Event-driven architecture (EDA) structures the system around the production, detection, and reaction to events. Instead of components calling each other directly, they emit events. Other components subscribe to the events they care about and react independently.

Event-driven architecture decouples producers from consumers through asynchronous events. A producer publishes an event to a broker or bus without knowing which consumers will process it. Consumers subscribe to event types and react independently.

graph LR OP[Order Placed] --> EB[Event Broker] EB --> INV[Inventory Service] EB --> PAY[Payment Service] EB --> NOT[Notification Service] EB --> ANA[Analytics Service] INV --> EB PAY --> EB

In this example, the order service publishes an "Order Placed" event. Four services react to it independently. The order service does not know or care how many consumers exist. Adding a fifth consumer (say, a fraud detection service) requires zero changes to the producer.

EDA comes in two topologies, as described in Mark Richards' Software Architecture Patterns:

The strength of EDA is loose coupling and extensibility. The weakness is that the overall flow of the system becomes harder to trace. When something goes wrong, you cannot follow a single stack trace. You follow events through a broker, across services, through time.

Microservices Architecture

Session 1.5 covered this in detail. As an architectural pattern, microservices decompose the system vertically by business capability rather than horizontally by technical layer. Each service owns its own data, logic, and interface for a specific domain.

The pattern's defining characteristic is independent deployability. If a service can only be deployed alongside other services, it is not a microservice. It is a distributed monolith, which inherits the costs of both architectures and the benefits of neither.

Microservices are often combined with event-driven architecture. Services communicate through events rather than direct API calls, which further reduces coupling and improves resilience.

Cell-Based Architecture

Cell-based architecture, documented in detail by WSO2's reference architecture, organizes the system into self-contained cells. Each cell is a complete unit that includes its own services, data stores, and communication gateway. Cells interact with each other through well-defined, versioned APIs at the cell boundary.

Cell-based architecture groups related services into autonomous cells, each with its own gateway, data stores, and internal communication. Cells are the unit of deployment, scaling, and failure isolation. Think of each cell as a small, self-sufficient system.

The cell boundary acts as a blast radius. If everything inside Cell A fails, Cell B is unaffected because it communicates only through the cell gateway, which can implement circuit breakers, retries, and fallbacks.

This pattern is particularly useful for very large systems where even microservices become hard to manage. Instead of managing 300 individual services, you manage 20 cells, each containing 10-20 services. The cell provides an intermediate level of organization between individual services and the overall system.

Serverless Architecture

Serverless architecture delegates all infrastructure management to a cloud provider. You write functions that execute in response to events (HTTP requests, message queue entries, file uploads, timers). The provider handles provisioning, scaling, and deprovisioning.

Serverless architecture runs application code in ephemeral, event-triggered functions managed entirely by a cloud provider. There are no servers to provision, patch, or scale. You pay only for the compute time consumed during execution.

Serverless excels at variable, unpredictable workloads. An image processing function that runs 10 times on Monday and 10,000 times on Friday costs proportionally. There is no idle capacity to pay for. The operational burden approaches zero for small to medium workloads.

The constraints are real. Functions have execution time limits (15 minutes on AWS Lambda). Cold starts add latency when a function has not been invoked recently. State must be stored externally. Vendor lock-in is significant because your function code is deeply coupled to the provider's event model, IAM system, and supporting services.

Pattern Comparison

Pattern Strengths Weaknesses Best Fit
Multi-tier Clear separation, well understood, easy to staff Rigid layering, uneven scaling, all-tier changes Traditional web apps, enterprise CRUD systems
Event-driven Loose coupling, extensible, async by default Hard to trace, eventual consistency, debugging complexity Systems with many independent reactions to the same trigger
Microservices Independent deploy, team autonomy, targeted scaling Network overhead, data consistency, operational burden Large teams, complex domains, varying scale per capability
Cell-based Blast radius control, organizational grouping, versioned boundaries Complex to design initially, over-engineering risk for small systems Very large systems with hundreds of services needing organizational structure
Serverless Zero ops, pay-per-use, automatic scaling Cold starts, execution limits, vendor lock-in, stateless Variable workloads, event processing, glue logic, prototypes

Combining Patterns

Production systems rarely use a single pattern in isolation. A typical e-commerce platform might use multi-tier for its web frontend, microservices for its backend capabilities, event-driven communication between those services, and serverless for image resizing and email sending. The patterns are not competing alternatives. They are complementary tools applied at different levels of the system.

The question is not "which pattern should we use?" It is "which pattern fits this specific part of the system, given our team size, operational maturity, and the constraints of the problem?"

Systems Thinking Lens

Each pattern creates a different feedback structure. Multi-tier creates long feedback loops (changes ripple through all layers). Event-driven creates many short, independent loops (each consumer reacts on its own). Microservices create team-level loops (each team iterates independently). Cell-based creates nested loops (within cells and between cells).

The pattern you choose determines where delays accumulate, where failures propagate, and where teams can move independently. These are structural decisions with compounding effects over months and years. A pattern that feels efficient today can create organizational gridlock in two years if the feedback loops it creates do not match how your teams actually work.

Further Reading

Assignment

Think about an application you work on or use frequently. It could be your company's product, an open-source project, or a well-known service like Grab, Gojek, or Spotify.

  1. Draw a box diagram of the major components. Keep it high-level: 5-10 boxes representing services, databases, queues, and external systems. Draw arrows showing how they communicate.
  2. Identify the primary pattern. Does it follow multi-tier, event-driven, microservices, cell-based, serverless, or a combination? What evidence in the diagram supports your answer?
  3. Find the pattern boundary. Most systems combine patterns. Identify at least one place where the system switches from one pattern to another (for example, request-response for the API layer but event-driven for background processing).

If you are not sure about the internals, make reasonable assumptions based on what you observe as a user. Where do you see real-time updates (event-driven)? Where do you see request-response behavior (multi-tier or microservices)? Where do you see background processing that does not block the user (serverless or event-driven)?