Unit of Work
debt(d7/e5/b7/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated detection is 'no', and while specialist tools like doctrine-profiler and laravel-debugbar can surface symptoms (N transactions, memory growth), they require deliberate profiling sessions rather than passive linting. The code patterns — flush() inside loops, missing rollbacks, memory growth in batch processing — are invisible in normal development and only surface under load or during careful review.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix sounds simple (move flush() outside loop), but common_mistakes reveal layered issues: incorrect transaction wrapping, mixed aggregate roots, misunderstood identity map behavior. Correcting all misuses typically requires restructuring how domain operations are batched and how repositories interact with the EntityManager across multiple files, not a single-line swap.
Closest to 'strong gravitational pull' (e7 mapped to b7). Unit of Work applies across web, cli, and queue-worker contexts and is tagged as architecture/ORM/database/patterns. Every domain operation, batch job, and repository method must be designed around how and when flush() is called and how transactions are scoped. This shapes every data-mutation flow in the codebase, imposing a persistent mental model requirement on all maintainers.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states developers believe Unit of Work is ORM-only, missing its broader applicability. Additionally, the common_mistakes show non-obvious behaviors: flush() inside loops causing N transactions, identity map stale state after failed flushes, and the same object returned from double-fetches. These are documented gotchas that experienced developers eventually learn but regularly catch newcomers.
Also Known As
TL;DR
Explanation
Unit of Work, described by Martin Fowler, maintains lists of objects affected by a business transaction: new, dirty (modified), and clean. At the end of the transaction, it coordinates persisting all changes in the correct order — inserts before updates, respecting foreign key constraints, within a single database transaction. ORMs like Doctrine implement Unit of Work automatically. Without it, you either make individual database calls per change (missing atomicity) or build transaction management ad hoc.
Common Misconception
Why It Matters
Common Mistakes
- Calling $em->flush() inside a loop — each flush is a database round trip; flush once after all changes.
- Not calling rollback on exception — Doctrine's Unit of Work holds changes in memory; a failed flush leaves the identity map stale.
- Mixing multiple aggregate roots in one Unit of Work transaction — each aggregate should commit independently.
- Not understanding that Doctrine's identity map is part of Unit of Work — fetching the same entity twice returns the same object.
Code Examples
// Flush inside loop — N database round trips:
foreach ($products as $product) {
$product->setPrice($product->getPrice() * 1.1);
$em->flush(); // 1 DB write per product — should be once at the end
}
// Batch all changes, single flush:
foreach ($products as $product) {
$product->setPrice($product->getPrice() * 1.1); // Marked dirty in UoW
}
$em->flush(); // One transaction, all products updated atomically