Data Access Patterns
Data access patterns define how application code interacts with persistent storage — databases, files, caches. They address the impedance mismatch between object-oriented domain models and relational or document-based storage, while managing concerns like identity, loading strategy, and data transfer boundaries.
Pattern Comparison
| Pattern | Purpose | Complexity | Best For |
|---|---|---|---|
| Active Record | Domain object handles its own CRUD | Low | Simple domains, rapid prototyping |
| Data Mapper | Separate mapper layer between domain and DB | High | Complex domains, clean architecture |
| Identity Map | Cache loaded objects by ID, avoid duplicates | Medium | ORM internals, unit-of-work |
| Lazy Loading | Defer data loading until first access | Medium | Object graphs with optional relations |
| DTO | Transfer data across boundaries without behavior | Low | API layers, serialization |
| Value Object | Immutable, identity-free domain values | Low | Money, coordinates, date ranges |
| Aggregate | Cluster of objects treated as a unit | High | DDD, transactional consistency |
Decision Guide
Is your domain model simple (few relations, straightforward CRUD)?
├─ Yes → Active Record
└─ No → Data Mapper + Identity Map
│
├─ Do objects have large relation graphs?
│ └─ Yes → Add Lazy Loading
│
├─ Do you pass data across layers/APIs?
│ └─ Yes → Use DTOs at boundaries
│
├─ Do you have small immutable concepts (money, email)?
│ └─ Yes → Model as Value Objects
│
└─ Do you need transactional consistency across multiple objects?
└─ Yes → Define Aggregates
Key Principles
- Separate concerns — Domain logic should not depend on storage mechanics.
- Manage identity explicitly — Know when two references point to the same entity.
- Load only what you need — Eager loading wastes resources; lazy loading risks N+1.
- Protect boundaries — Don't leak domain objects into API responses (use DTOs).
- Design for consistency — Aggregates define transactional boundaries.