romanzipp/laravel-projectable-aggregates
Laravel package for projecting aggregate values such as counts, sums, and averages from related Eloquent models onto a parent model. Define projections declaratively and keep denormalized columns in sync automatically, avoiding repeated aggregate queries and simplifying reporting.
Feature feasibility for aggregate-heavy surfaces. Any user-facing or admin-facing screen that displays counts, sums, averages, or totals against a parent record — dashboard tiles, "12 open tickets," "4.8 average rating," customer lifetime value, inventory totals, unread badges, "top customers by order count" leaderboards — becomes cheap to render. This is the core PM value: it turns a class of "we can't ship that, the query is too slow" conversations into "what number should the tile show?"
Roadmap sequencing: a tactical lever, not a strategic one. If aggregate queries are a known scaling bottleneck, this package gives you a low-cost option to defer or avoid a much larger initiative — a materialized-view migration, a read replica, a dedicated reporting/warehouse store. Use it to buy time and validate demand for the dashboard/reporting surface before committing engineering budget to a heavier data architecture. Do not let it become a reason to skip the strategic conversation if the numbers keep growing.
Build vs buy on a deceptively simple pattern. The idea (denormalized aggregate columns kept in sync via model events) is an afternoon of work to prototype. The edge cases are not: updates, deletes, bulk operations, soft deletes, records changing parents, concurrency on hot rows. The real decision is whether you want to own and test that logic or adopt a small, MIT, near-zero-adoption library and own it. Both are defensible; the wrong answer is assuming it's free.
Prioritization of "fast list" and admin/reporting views. Internal tooling, sortable/filterable list screens, and admin dashboards are the ideal pilot surface: high aggregate-read volume, low blast radius if a number is briefly stale, and immediate visible payoff (page loads). They are also where you can measure value cheaply before touching customer-facing numbers.
Scoping of data-integrity and reconciliation work. Adoption forces an explicit product decision about staleness tolerance: how wrong may a displayed number be, and for how long? That answer determines whether a nightly reconciliation job and a drift alert are a nice-to-have or a launch blocker. Product, not just engineering, should sign off on that SLO — it's the same conversation as "is it OK if the badge is one minute behind?"
Forcing function on read-path consistency. Because projection state is a cache with a truth source elsewhere, adopting this surfaces latent assumptions ("we always write through Eloquent") that were never written down. That's valuable even if you don't adopt the package.
Good fit:
withCount/withSum or subqueries appear in slow-query logs and are trending up.Look elsewhere if:
Mature alternatives to weigh at either end of scale: database materialized views, Redis counters, a dedicated reporting store, or simply withCount/withSum with proper indexing.
For executives: We keep hitting the same class of feature — dashboards, badges, rankings, admin lists — that needs fast totals. Today each one either fires an expensive query or gets hand-built from scratch. This is a small, MIT-licensed Laravel library that stores those totals alongside the record and keeps them updated automatically, which means faster pages and less bespoke code per feature. It is a tactical lever to defer larger data-infrastructure spending, not a replacement for it: if these numbers keep growing, we will eventually need the reporting store or read replica. The honest caveat is maturity, not license or lock-in: it is a low-adoption, single-maintainer project, so we should treat it as code we own. Recommendation: pilot on one internal screen behind a flag, validate against the existing query with shadow comparison, fork it if it proves useful, and keep a nightly reconciliation job in place. Cost is near zero; the risk we are accepting is maintenance ownership, not vendor dependence.
For engineering: Adopt the pattern; be skeptical of the dependency. The value is a conventional, trait-based implementation of Eloquent-driven aggregate projections (counts, sums, averages) — with the interesting part being whether the update/delete/soft-delete/bulk-write paths are actually handled. Before we commit: audit the source for bulk-write and soft-delete handling and for atomic increments vs. read-modify-write on hot parent rows; confirm the Laravel/PHP version matrix; pin or vendor the package; add a nightly recompute command; and add tests anywhere a child record can be reparented. Sequence it as trait + columns (no reads) → shadow comparison → backfill → read cutover → reconciliation → expand to more models. Do not put it in a financial, billing, or quota path. If the audit is clean, this removes a chunk of bespoke observer code from the backlog; if not, the design is small enough to reimplement in a sprint — and either way the team needs to internalize the rule that these columns are caches with a truth source elsewhere, encoded in a short ADR and a "never mutate these tables outside Eloquent" convention.
How can I help you explore Laravel packages today?