Software Design and Architecture Patterns
Table of Contents
1. Overview
The year marked a significant acceleration in cloud-native adoption, microservices maturity, and the formalization of architecture decision-making processes.
2. Background
- The pandemic-driven acceleration of digital transformation
- Kubernetes reaching mainstream production adoption
- Growing complexity of distributed systems requiring better tooling
- Increased focus on developer productivity and platform engineering
- Service mesh technologies maturing for production use
- The rise of GitOps and infrastructure as code as standard practice
3. Key Concepts
3.1. Microservices Patterns
3.1.1. Domain-Driven Design (DDD) Alignment
- Bounded Contexts as service boundaries became the standard approach
- Context Mapping patterns (Anti-Corruption Layer, Open Host Service) guided integration
- Aggregates defined transaction boundaries within services
- Domain Events enabled loose coupling between contexts
3.1.2. Service Decomposition Strategies
| Strategy | Description | When to Use |
|---|---|---|
| Strangler Fig | Gradually replace monolith components | Legacy modernization |
| Business Capability | Align services to business functions | Greenfield projects |
| Subdomain-based | Follow DDD subdomains | Complex domains |
| Team-oriented | Conway's Law alignment | Large organizations |
3.1.3. Data Management Patterns
- Database per Service: Each microservice owns its data store
- Saga Pattern: Distributed transactions across services using choreography or orchestration
- CQRS: Command Query Responsibility Segregation for read/write optimization
- Event Sourcing: Storing state changes as immutable event sequences
3.2. Event-Driven Architecture
3.2.1. Core Patterns
3.2.1.1. Event Notification
Simple events signaling that something occurred, allowing subscribers to query for details:
OrderPlaced { orderId: "12345", timestamp: "2021-12-01T10:00:00Z" }
3.2.1.2. Event-Carried State Transfer
Events containing all necessary data, reducing service coupling:
OrderPlaced {
orderId: "12345",
customer: { id: "c789", name: "..." },
items: [...],
total: 150.00
}
3.2.1.3. Event Sourcing
Persisting domain events as the source of truth:
AccountCreated -> FundsDeposited -> FundsWithdrawn -> FundsDeposited
3.2.2. Message Broker Technologies (2021 Landscape)
| Technology | Strengths | Use Cases |
|---|---|---|
| Apache Kafka | High throughput, durability, replay | Event streaming, log aggregation |
| RabbitMQ | Flexible routing, mature ecosystem | Task queues, RPC |
| AWS SNS/SQS | Managed, scalable | Cloud-native applications |
| NATS | Lightweight, fast | Edge computing, IoT |
| Pulsar | Multi-tenancy, tiered storage | Large-scale streaming |
3.2.3. Eventual Consistency Handling
- Idempotency Keys: Ensuring operations can be safely retried
- Outbox Pattern: Reliable event publishing with transactional guarantees
- Inbox Pattern: Deduplication of incoming messages
- Compensation Events: Undoing operations in saga failure scenarios
3.3. Cloud-Native Design
3.3.1. The Twelve-Factor App (Extended)
The original twelve factors remained foundational, with additional emphasis on:
- Codebase: One codebase tracked in revision control, many deploys
- Dependencies: Explicitly declare and isolate dependencies
- Config: Store config in the environment
- Backing Services: Treat backing services as attached resources
- Build, Release, Run: Strictly separate build and run stages
- Processes: Execute the app as stateless processes
- Port Binding: Export services via port binding
- Concurrency: Scale out via the process model
- Disposability: Maximize robustness with fast startup and graceful shutdown
- Dev/Prod Parity: Keep development, staging, and production similar
- Logs: Treat logs as event streams
- Admin Processes: Run admin/management tasks as one-off processes
3.3.2. Beyond Twelve-Factor (2021 Additions)
- API First: Design APIs before implementation
- Telemetry: Built-in observability (metrics, traces, logs)
- Security: Zero-trust, secrets management, mTLS
- Authentication: Externalized identity management
3.3.3. Service Mesh Architecture
Service meshes (Istio, Linkerd, Consul Connect) provided:
- Traffic Management: Load balancing, circuit breaking, retries
- Security: mTLS, authorization policies
- Observability: Distributed tracing, metrics collection
- Traffic Splitting: Canary deployments, A/B testing
3.3.4. Observability Pillars
Observability
|
+----------------+----------------+
| | |
Metrics Traces Logs
(Prometheus) (Jaeger) (ELK/Loki)
| | |
+----------------+----------------+
|
Dashboards
(Grafana)
3.4. Architecture Decision Records (ADRs)
3.4.1. Purpose and Benefits
ADRs document significant architectural decisions in a lightweight, version-controlled format:
- Context Preservation: Why decisions were made
- Onboarding: New team members understand history
- Governance: Track decision approval and review
- Evolution: Decisions can be superseded with references
3.4.2. ADR Template (Michael Nygard Format)
# ADR-001: Use Event Sourcing for Order Management
## Status
Accepted
## Context
We need to maintain a complete audit trail of all order state changes
and support temporal queries for compliance reporting.
## Decision
We will implement event sourcing for the Order aggregate, storing all
domain events in an append-only event store.
## Consequences
- Complete audit trail of all changes (positive)
- Ability to rebuild state at any point in time (positive)
- Increased complexity in read model management (negative)
- Need for event versioning strategy (negative)
- Team requires training on event sourcing patterns (negative)
3.4.3. ADR Lifecycle States
| State | Description |
|---|---|
| Proposed | Under discussion |
| Accepted | Approved for implementation |
| Deprecated | No longer recommended |
| Superseded | Replaced by another ADR |
3.4.4. ADR Tooling
All three tools predate this note's Dec 2021 date and match their described function. Per GitHub repo metadata: npryce/adr-tools (the CLI) was created 2016-02-04; thomvaill/log4brains was created 2020-09-15. Structurizr's ADR integration is real -- the structurizr/java commit history shows "Added support for ADRs" on 2018-08-11, exposed via the `!adrs` DSL keyword (per docs.structurizr.com/dsl/language), so the C4-model/decisions link was already three years old by the time of this note.
adr-tools: CLI for managing ADR filesLog4brains: Architecture knowledge base from ADRsStructurizr: C4 model integration with decisions
4. Implementation
4.1. Recommended Project Structure
project/
├── docs/
│ └── adr/
│ ├── 0001-record-architecture-decisions.md
│ ├── 0002-use-postgresql-for-persistence.md
│ └── 0003-adopt-event-driven-messaging.md
├── services/
│ ├── order-service/
│ ├── inventory-service/
│ └── notification-service/
├── infrastructure/
│ ├── kubernetes/
│ └── terraform/
└── shared/
├── events/
└── contracts/
4.2. Technology Stack Recommendations (2021)
| Layer | Technology Options |
|---|---|
| Container Orchestration | Kubernetes, ECS, Nomad |
| Service Mesh | Istio, Linkerd, Consul Connect |
| Message Broker | Kafka, RabbitMQ, Pulsar |
| API Gateway | Kong, Ambassador, Traefik |
| Observability | Prometheus + Grafana, Datadog, New Relic |
| Secrets Management | HashiCorp Vault, AWS Secrets Manager |
| CI/CD | GitHub Actions, GitLab CI, ArgoCD |
| Infrastructure | Terraform, Pulumi, CDK |
4.3. Anti-Patterns to Avoid
- Distributed Monolith: Microservices with tight coupling and synchronous dependencies
- Nano-services: Over-decomposition leading to excessive network overhead
- Shared Database: Multiple services accessing the same database schema
- Big Ball of Mud: Lack of clear boundaries and responsibilities
- Golden Hammer: Using one pattern for all problems
- Resume-Driven Development: Choosing technologies for career advancement over project needs
5. References
Checked the dated citations against primary sources rather than taking the years at face value. Nygard's piece is a blog post, not a book -- confirmed at cognitect.com/blog/2011/11/15/documenting-architecture-decisions.html, dated 2011-11-15, matching "(2011)". Vernon's "Implementing Domain-Driven Design" publisher page (informit.com, Addison-Wesley) lists Feb 6, 2013, matching "(2013)" (a secondary aggregator had shown 2012, but the publisher's own date confirms 2013). Richardson's "Microservices Patterns" (2018) and Kleppmann's "Designing Data-Intensive Applications" (2017) both check out against Open Library's first_publish_year. Newman's 2nd edition "(2021)" checks out against isbnsearch.org's ISBN-13 9781492034025 record (O'Reilly, 2nd edition, 2021) -- notable only because it lands in the same month/year as this note (2021-12-01), the narrowest margin of any citation here.
- Newman, Sam. "Building Microservices" 2nd Edition (2021)
- Richardson, Chris. "Microservices Patterns" (2018)
- Kleppmann, Martin. "Designing Data-Intensive Applications" (2017)
- Nygard, Michael. "Documenting Architecture Decisions" (2011)
- Vernon, Vaughn. "Implementing Domain-Driven Design" (2013)
- The Twelve-Factor App: https://12factor.net/
- Cloud Native Computing Foundation: https://www.cncf.io/
- ADR GitHub Organization: https://adr.github.io/
6. Notes
- The shift from "microservices as default" to "right-sized services" reflected growing maturity
- Platform engineering emerged as a discipline to manage infrastructure complexity
- GraphQL federation gained traction for API gateway patterns
- WebAssembly (Wasm) began appearing in edge computing and serverless contexts
- Supply chain security (SBOM, signing) became critical after high-profile incidents
- Internal developer platforms (IDPs) reduced cognitive load on application teams