Architecture Patterns
39 patternsReusable solutions to recurring backend architecture problems. Each pattern includes explicit tradeoffs, applicability conditions, operational requirements, and scaling limits.
39 items
Provide a single, managed entry point for all client requests that handles routing, authentication, rate limiting, protocol translation, and observability : decoupling clients from the internal service topology.
A flow control mechanism where a downstream system signals its capacity limit to upstream producers, causing them to slow or pause ingestion rather than accumulating unbounded queues that eventually exhaust memory or corrupt data.
Maintain two identical production environments (blue and green). Route all traffic to the active environment while deploying and validating the new version in the idle environment. Swap traffic atomically when validation passes, enabling instant rollback by swapping back.
Partition resource pools: thread pools, connection pools, memory, or queue capacity: so that exhaustion or failure within one partition cannot consume resources needed by unrelated operations.
Application code manages the cache explicitly: check cache first, fetch from the database on a miss and populate the cache, and invalidate or update the cache on writes: with no automatic synchronisation layer between the two.
Stream database changes in real-time by reading the write-ahead log, enabling downstream consumers to react to inserts, updates, and deletes without polling or dual writes.
Detect repeated failures to a downstream dependency and stop attempting calls to it for a recovery window, preventing cascading failure and allowing the dependency time to recover.
Physically store table data grouped by column rather than by row, so an analytical query that touches a fraction of a table's columns reads only those columns from disk, at the cost of making single-row reads and writes far more expensive than in a row-store.
Multiple consumer instances read from the same queue or topic; each message is processed by exactly one consumer, scaling processing throughput by adding consumers without changing the producer.
Maintain a pool of reusable database connections shared across application threads, eliminating the per-request cost of establishing new connections.
Map both cache or storage nodes and data keys onto the same circular hash ring so that adding or removing a node reshuffles only (1/N) of all keys rather than requiring a full rehash of all data.
Separate the write model (commands that mutate state) from the read model (queries that return data), allowing each side to be optimised, scaled, and evolved independently.
Assign each service exclusive ownership of its own database schema or instance, prohibiting direct cross-service database access and enforcing data encapsulation at the service boundary.
Store every state change as an immutable, ordered event appended to an event store; derive current state by replaying the event log rather than overwriting rows in place.
Embed the current state of the changed entity directly in each event payload ("fat event"), so consuming services can update their local read models without making synchronous API calls back to the source service: eliminating downstream query fan-out and reducing coupling between services.
Compute a user's feed or aggregated view at read time by fetching and merging the most recent content from all followed accounts, rather than pre-computing and materializing the feed at write time: trading higher read latency for dramatically lower write amplification, especially for accounts with millions of followers.
On write, precompute and push the result to all relevant readers' caches or feed stores. Reads become pure cache lookups with no aggregation, shifting latency cost from read time to write time.
Index geographic coordinates using space-partitioning data structures (R-tree, GeoHash, S2, PostGIS GiST) to enable efficient nearest-neighbor and radius queries : finding all records within N kilometers of a point without scanning every record.
Expose a dedicated endpoint that reports service health and critical dependency status so that load balancers and orchestrators can route traffic away from unhealthy instances and restart failed ones.
Guarantee idempotent message processing in a consumer by recording each message's unique identifier in a local "inbox" table, in the same transaction as the message's side effect, before that side effect runs. A redelivered message finds its ID already recorded and is skipped, so the side effect happens exactly once even though the broker may deliver the message more than once.
Maintain a secondary lookup table that maps a non-primary-key attribute to primary keys, enabling efficient queries that the primary key structure does not support without requiring full-table scans.
Elect a single active node among a group of distributed replicas to perform writes, coordinate state, or execute singleton tasks, preventing conflicting operations from multiple concurrent actors. The guarantee this actually provides depends entirely on the election mechanism: consensus-based election can be made linearizable, lease-based election cannot, no matter how carefully it is tuned.
Store data as an in-memory memtable backed by a write-ahead log, periodically flushed to immutable sorted-string-tables (SSTables) on disk and merged by background compaction, trading point-read latency and background I/O for sequential-only writes and sustained high write throughput.
Pre-compute and persist the result of an expensive query as a physical table or document, serving subsequent reads from the pre-computed result rather than re-executing the query on every request.
Producers publish events to a topic or channel without knowing subscribers. All interested subscribers receive every event, decoupling producers from consumers at both the semantic and temporal level.
Control the rate at which requests from a given client, tenant, or IP address are accepted by enforcing a maximum request count over a sliding or fixed window, protecting downstream systems from overload while preserving capacity for well-behaved callers.
Distribute read queries across one or more synchronous or asynchronous replicas of a primary database node, reducing read load on the primary.
Place a cache in front of the database such that all reads go to the cache; on a miss, the cache itself fetches the data from the database, populates itself, and returns the result: the application never reads the database directly and cache population is handled automatically by the cache layer.
Retry transient failures with exponentially increasing wait times and added jitter to prevent synchronized retry storms while ensuring eventual operation success without saturating a recovering dependency.
Run a business transaction that spans multiple services as a sequence of local transactions, each atomic in its own service and each paired with a compensating transaction that semantically reverses it if a later step fails. It trades isolation for service autonomy: concurrent sagas can observe each other's intermediate state.
Horizontally partition a dataset across multiple independent database nodes by a shard key, distributing both storage and query load so that each node handles only a fraction of the total data.
Periodically capture the full materialized state of an event-sourced aggregate into a snapshot record, so that subsequent reads reconstruct the aggregate from the most recent snapshot plus a short tail of events: rather than replaying the entire event history from the beginning.
Incrementally replace a legacy system by routing subsets of its traffic to a new implementation while the legacy system remains live, eliminating the need for a high-risk big-bang migration.
Partition a multi-tenant system's data and resources such that one tenant's activity, load spikes, or data cannot affect the correctness or performance of other tenants: choosing from a spectrum of isolation models from shared schema through dedicated infrastructure.
Periodically aggregate raw time series data into coarser-grained summary rows (e.g., per-second metrics → per-minute → per-hour → per-day), then delete or compress the raw data, maintaining query performance and bounded storage growth as the dataset ages.
Atomically persist a business state change and its outgoing event in one database transaction, then relay that event to the message broker asynchronously. The event and the state it describes commit or roll back together, which closes the dual-write gap between database and broker.
Commit one transaction atomically across several independent database participants using a prepare phase (every participant votes that it can commit) followed by a commit phase (the coordinator tells all to commit or all to abort). It buys all-or-nothing across nodes at the cost of availability: a coordinator failure leaves prepared participants blocked.
Store high-dimensional embedding vectors alongside records and serve approximate nearest-neighbor (ANN) queries using specialized indexes (HNSW, IVF, FAISS), enabling semantic similarity search: finding records whose meaning is close to a query, not just records whose text matches exactly.
Acknowledge writes to cache immediately and flush to the database asynchronously, so write latency seen by the caller is cache write latency rather than database write latency.