persistence-patterns

Installation
SKILL.md

Persistence, Mappers & Repositories in PHP

When to Use Rich Persistence vs. Simple ORM

Rich persistence (Mapper + Repository) is only worth the ceremony when the application has complex invariants or needs to swap storage. Use simple ORM for straightforward CRUD.

Use Rich Persistence (Mapper + Repository) Use Simple ORM (Entity + Query Builder)
Complex business invariants to protect Single-table CRUD with few rules
Need to swap storage implementation Fixed database schema
Tests must run without DB for domain logic Schema changes hand-in-hand with feature
Long-lived system, evolving requirements Prototype, MVP, throwaway code

A library or package that simply exposes data objects should not include mappers or repository interfaces.

Data Source Spectrum & Gateway vs Repository

Persistence structure is a separate axis from domain-logic style. There is a progression of increasing separation between business logic and storage: Table Data Gateway → Row Data Gateway → Active Record → Data Mapper (see data-source-patterns.md for the spectrum, philosophy, and Gateway-vs-Repository distinction). The lightest end (Table Data Gateway) is one class per table that fetches/persists rows and nothing more; the heavy end (Data Mapper) fully isolates domain objects from the database. Active Record sits in the middle: one object per row that also carries its business behavior and knows how to persist itself. It is outstanding for CRUD-heavy apps with modest rules and small teams that value rapid iteration, but becomes a liability as the domain grows richer and more interconnected — the class drifts from representing the business to representing the ORM. Recognize when it has outgrown its limits and move persistence/mapping out into a Data Mapper. At the heavy end, Data Mapper fully isolates domain objects from the database (only the Mapper bridges the two); it is the preferred persistence model for complex enterprise software (DDD, Hexagonal/Onion/Clean, CQRS writes) because it keeps database concerns out of the domain. Its value is not easy database swaps — it is persistence ignorance for a rich domain model — and the extra indirection is only justified when a rich domain would otherwise be compromised by persistence concerns.

Installs
41
GitHub Stars
15
First Seen
Jul 15, 2026