Architectural Patterns

Purpose

Architectural patterns provide proven, reusable solutions to common structural problems in software systems.

Understanding these patterns enables engineers to recognize recurring problems, apply known solutions, and communicate architectural decisions using a shared vocabulary.

This guide explains the most common architectural patterns, their trade-offs, and the conditions under which each should be considered.

Background

Architecture is the set of design decisions that are costly to change.

Architectural patterns are not prescriptions. They are tools. Each pattern optimizes for different outcomes and introduces different constraints. Choosing a pattern means accepting its trade-offs.

The patterns described in this guide are technology-independent. They can be implemented in any language, framework or infrastructure.

How to Choose a Pattern

Choosing an architectural pattern requires understanding the problem context:

No single pattern is universally superior. The best pattern is the one that best aligns the architecture with the business goals.

Pattern Catalogue

Layered Architecture

Description

Organize the system into horizontal layers, each with a specific responsibility. Each layer depends only on the layer directly beneath it.

  Presentation
       ↓
   Application
       ↓
     Domain
       ↓
 Infrastructure

When to Use

When to Avoid

Trade-offs

ProCon
Simple and widely understoodCan lead to monolithic deployments
Clear separation of concernsLayer bypass undermines the pattern
Easy to get startedTendency toward code duplication
Good for small to medium systemsDoes not scale well organizationally

Hexagonal Architecture (Ports and Adapters)

Description

Isolate core business logic from external concerns through ports (interfaces) and adapters (implementations).

The core domain has no dependency on databases, APIs, UIs, or any other external system. All external communication flows through adapters that implement ports defined by the core.

  [Web Adapter] → [Port] → [Core Domain] ← [Port] ← [Database Adapter]
  [CLI Adapter]  → [Port] →              ← [Port] ← [Message Queue Adapter]

When to Use

When to Avoid

Trade-offs

ProCon
Domain is highly testableIncreased indirection and complexity
External systems are swappableMore interfaces to maintain
Business logic is framework-freeCan be over-engineering for simple apps
Strong separation of concernsRequires discipline to maintain

Event-Driven Architecture

Description

Components communicate through events rather than direct calls.

Producers emit events without knowing which consumers will handle them. Consumers subscribe to events without knowing which producers emitted them. This creates loose coupling between components.

  [Order Service] ──→ (OrderPlaced) ──→ [Inventory Service]
                         │
                         ├──→ [Notification Service]
                         └──→ [Analytics Service]

When to Use

When to Avoid

Trade-offs

ProCon
Strong decoupling between servicesEventual consistency is complex
Highly scalable and extensibleDebugging is harder than request*response
Real-time processing capabilityEvent schema evolution is challenging
Good for heterogeneous systemsRequires robust monitoring

Microservices Architecture

Description

Compose the system from independently deployable services, each owning its own data and domain. Services communicate through well-defined APIs (generally HTTP or messaging).

  [API Gateway]
     ├── [Order Service] ─→ [Order DB]
     ├── [Inventory Service] ─→ [Inventory DB]
     ├── [Payment Service] ─→ [Payment DB]
     └── [Notification Service]

When to Use

When to Avoid

Trade-offs

ProCon
Independent deployabilityOperational complexity
Team autonomyData consistency challenges
Technology heterogeneityNetwork overhead and latency
Independent scalingTesting across services is harder

CQRS (Command Query Responsibility Segregation)

Description

Separate read and write operations into different models. Commands handle state changes. Queries handle data retrieval.

The separation allows each model to be optimized independently.

When to Use

When to Avoid

Trade-offs

ProCon
Optimized read and write modelsIncreased complexity
Independent scaling of reads*writesEventual consistency between models
Better performance for each workloadMore code to maintain

Pattern Composition

Architectural patterns are not mutually exclusive.

Common compositions include:

Compose patterns only when the combination clearly addresses a specific problem. Every added pattern increases complexity.

AI Integration

AI assistants can help with architectural patterns by:

When asking AI for architectural pattern advice, provide:

Related Documents