somnambulist/read-models
Read models for Laravel: lightweight, query-optimized projections for CQRS applications. Build dedicated read-side models from domain events, separate reads from writes, and keep query performance predictable without loading full aggregates.
Architecture fit. somnambulist/read-models implements a lightweight CQRS-flavoured read layer: instead of hydrating full Eloquent models, you declare a ReadModel class bound to a table (or a hand-written query/join), map columns, and query through a stripped-back active-record API (::where(), ::orderBy(), eager-loaded relations, pagination). For a Laravel/PHP codebase that is read-heavy and Eloquent-bound, this is architecturally coherent — it sits on the same PDO connection, requires no new infrastructure, and lets you bypass model events, global scopes, mutators and serialisation overhead on hot read paths. It is not an event-sourced or materialised-view CQRS toolkit; treat it as a disciplined query-object pattern, not a projection engine.
Integration feasibility. Low friction. It is a standard Composer package (MIT), PSR-4 autoloaded, with a Laravel service provider — expected integration is composer require somnambulist/read-models, then defining read-model classes alongside existing Eloquent models. Because it reads from the same database, no dual-write, replication or sync problem exists; existing tables are the source of truth. This is the package's main draw: you can introduce it in a single endpoint without touching the write model.
Technical risk. The risk is almost entirely supply-chain and coverage, not design. Signals: 12 GitHub stars, 0 known dependents, a single-maintainer lineage (Somnambulist Tech), and a last tagged release of March 2024 — sparse cadence. At 0.16 the composite score is low, and the 3.42 "opportunity" score is the interesting counter-signal: the problem space (lean read paths, read/write separation in Laravel) is real and underserved. Concrete risks to validate: (1) column maps are hand-maintained, so schema drift produces silent wrong-data bugs rather than exceptions; (2) join/alias handling and relation mapping are the most common source of subtle defects in this class of library; (3) Laravel major-version compatibility is unproven beyond the versions tested at release time; (4) 0 dependents means you are effectively the first production reference — no community-derived bug surface.
Key questions.
composer.json, and does that cover our target (PHP 8.2/8.3, Laravel 10/11)?with() does?DB::transaction() and read/write connection splitting, or does it pin a connection?Stack fit. Excellent on paper: same framework, same ORM-adjacent ergonomics, same connection pool, no new runtime. It complements rather than replaces Eloquent — writes stay on Eloquent (validation, events, observers, policies), reads move to read models where latency and payload shape matter. This is a both/and integration, which keeps blast radius small.
Migration path. Use a strangler pattern. Identify 1–3 high-traffic, read-only endpoints (list/index pages, dashboards, API collection resources) whose Eloquent models are doing heavy lifting — global scopes, accessors, eager loads, serialisation. Reimplement those as read models behind the existing controller/resource boundary so the HTTP contract is unchanged. Keep Eloquent authoritative everywhere else. Do not attempt a wholesale replacement of models; the package is explicitly not a write model and gains nothing from broad adoption.
Compatibility. Verify three things before committing: Composer constraint resolution against our Laravel version (fork or constrain if it lags), DB driver parity for our engines (MySQL vs PostgreSQL differ on ordering/null handling and identifier quoting), and behavioural parity with the current endpoint — same JSON shape, same pagination metadata, same filter semantics. Where the package's API is narrower than Eloquent's, document the divergence rather than papering over it.
Sequencing.
Maintenance. Small surface area, which cuts both ways: few moving parts to break, but also few people to fix them. Pin the exact version in composer.json, require a green CI run before any bump, and budget for occasional self-patching. MIT licensing makes forking and vendoring legally trivial — this is the key de-risking lever for a 12-star dependency. Assume you may own it.
Support. Single-maintainer, low-issue-volume project. Do not expect fast upstream turnaround on framework-compatibility PRs; plan for the team to absorb that. Internal documentation of our read-model conventions (naming, column maps, testing pattern) is therefore mandatory, not optional.
Scaling. Neutral-to-positive. It is SQL over the existing connection, so it scales exactly as the database does — no new hop, no cache layer, no serialisation service. The win is reduced hydration and serialisation cost per request; the ceiling is still the database. If read volume grows, the correct next lever remains read replicas/caching, not this library.
Failure modes.
Ramp-up. Shallow learning curve — a Laravel developer is productive in under a day. The real cost is conceptual: the team now has two data-access idioms, and the boundary ("when is a read model appropriate?") must be written down or it will drift into inconsistent codebases. Recommend a short internal guideline plus a code-review checklist item, and treat the pilot as the reference implementation. Overall verdict: low-cost, low-risk-if-containable experiment with genuine upside on read-heavy paths; not a platform bet. Proceed to pilot with an explicit fork-and-own contingency.
How can I help you explore Laravel packages today?