# Data Patterns

> Patterns for keeping application data honest at speed and across services: copies that exist only to be lost, multi-step transactions with explicit undos, and messages that cannot be lost or doubled.

- kind: category
- category: design-patterns
- canonical: https://aipatterns.dev/design-patterns/data
- inspired-by: https://learn.microsoft.com/en-us/azure/architecture/patterns

## What these patterns solve

An application that scales up its reads eventually holds data in more than one place, and every extra copy is a way to be wrong. A workflow that spans services cannot share a transaction, and a state change that must notify the world has two ways to fail - silently losing the message, or delivering it twice. These patterns fix the division of labor: the store decides the truth, the copies accelerate it, workflows get all-or-nothing semantics without locks, and messages become data before they become side effects.

**The rule they all enforce: writes land in the source of truth first; anything else is a disposable speedup - and every copy or message says exactly how stale or how repeated it may be.**

## Members

- [Cache-Aside](https://aipatterns.dev/design-patterns/cache-aside.md): load into a cache on miss, save to the store and evict on write - so reads speed up while the store stays the only source of truth.
- [Transactional Outbox](https://aipatterns.dev/design-patterns/transactional-outbox.md): write the outgoing message inside the business transaction; a relay publishes it afterwards - the commit and the message are one fact.
- [Saga](https://aipatterns.dev/design-patterns/saga.md): run a multi-service workflow as local steps with compensations, undone in reverse on failure - consistency without distributed locks.
- [Idempotent Consumer](https://aipatterns.dev/design-patterns/idempotent-consumer.md): claim each message id in the processing transaction, so at-least-once delivery produces exactly-once effects.
- [CQRS](https://aipatterns.dev/design-patterns/cqrs.md): separate the command model from per-view read models joined by projections - each shaped for its job, at the price of two models and read lag.

## How to choose

- **Reads dominate, the query is expensive, and slightly stale answers are acceptable:** [Cache-Aside](https://aipatterns.dev/design-patterns/cache-aside.md). Give every entry a TTL and evict on write - never write to both copies and hope.
- **A state change must reliably produce a message:** [Transactional Outbox](https://aipatterns.dev/design-patterns/transactional-outbox.md). The outbox row commits with the change; never send from inside or just after a transaction.
- **A workflow spans services and must be all-or-nothing:** [Saga](https://aipatterns.dev/design-patterns/saga.md). Name every compensation first; a step without one goes last or becomes retriable.
- **Delivery is at-least-once anywhere in the path:** [Idempotent Consumer](https://aipatterns.dev/design-patterns/idempotent-consumer.md) on every counting handler - outbox relays, webhook providers, and queue redelivery all guarantee duplicates.
- **Reads and writes measurably fight over one model, on one contentious subdomain:** [CQRS](https://aipatterns.dev/design-patterns/cqrs.md). Elsewhere, CRUD; be very cautious.
- **The value must always be current for correctness and lives in one store:** none of these. Read the store.

## Prefer no pattern when

- The store is in-process and already fast - a map beside your map is not a cache layer.
- Writes are as frequent as reads; the invalidation overhead cancels the read savings.
- The whole workflow fits one database transaction - a saga re-implements it without the isolation.
- You have not measured the query you are about to cache, or the contention you are about to split.
