~/fz.benhammou
← work

Stock Tracker

Event-sourced inventory: immutable movements, stock levels derived from the event log, plus a measured N+1 fix.

Stock Tracker

An inventory system built on a simple rule: movements are the data, stock is a view. Every change is an immutable IN / OUT / ADJUST movement row; stock on hand is computed from the log, never stored as mutable state. A Lumen REST API is the source of truth, and a PHP CLI talks to it.

Problem

GET /movements returned the right data, but the list endpoint silently ran extra queries per row to fetch each movement's product and location. Nothing looked wrong in the output. The only symptom was the query count growing with the list.

Root cause

The row-enrichment loop touched $movement->product and $movement->location on every row. With lazy loading, each access fires a query: for 31 movements, that is 1 list query + 31 product queries + 31 location queries = 64 queries for one page.

Design decisions

Result

Measured with nplus1-check.php in the repo, it runs the controller's list code twice, lazy and eager, and counts queries:

GET /movements, 31 rows
  lazy:  64 queries
  eager: 4 queries

What I would change

The before/after lives on nplus1/start → nplus1/solution, and the repo ships the seeded demo database so the measurement runs out of the box.