gdbots/ncr
gdbots/ncr is a Composer-installable PHP package from gdbots for NCR-related functionality in Laravel/PHP applications. It offers a lightweight integration point for projects using the gdbots ecosystem, with straightforward setup and compatibility.
Architecture fit. gdbots/ncr is a framework-agnostic content-node repository abstraction, not a Laravel package. It defines a NodeRepository contract over "nodes" (content entities with a UUID NodeId, slug, etag, curation status, and event-sourced writes via gdbots/pbjx). In a Laravel codebase it does not sit underneath Eloquent — it competes with it for ownership of the content domain. Fit is strong only if the product genuinely wants a schema-first content model (PBJ schemas compiled to PHP classes), node references, curation/publishing workflow, and swappable storage backends. Fit is poor for teams that rely on Eloquent conventions, Nova, Scout, model factories, and the broader Laravel content ecosystem, because NCR replaces those primitives rather than integrating with them.
Integration feasibility. Composer-installable, PSR-4, Apache-2.0 — legally and mechanically easy to adopt. The real cost is the transitive surface: gdbots/pbj (message/schema runtime), gdbots/pbjx (event bus, service locator), gdbots/common, plus a storage driver (gdbots/ncr-eloquent for SQL, gdbots/ncr-dynamodb for DynamoDB). There is no Laravel service provider, facade, or config stub; the team writes all container bindings, config, and lifecycle wiring. A schema compilation step (PBJ codegen) must be added to the build pipeline, which is unusual for a Laravel shop and will need CI work.
Technical risk. High, driven almost entirely by ecosystem maturity rather than code quality. Zero stars and zero dependents means effectively no external production validation, no Stack Overflow corpus, no community debugging. Bus factor is concentrated in a single vendor. Documentation is README-plus-source; onboarding cost is real. Version constraints on PHP and illuminate/database may lag current Laravel majors, creating a pinning treadmill. The DynamoDB path introduces AWS coupling that is hard to reverse. v3.1.2 is a narrow patch, not a risk-profile change: it hardens GetNodeRequestHandler so that aggregate-synced nodes with empty event streams are no longer treated as valid. That is a read-path correctness fix in exactly the area (write/event divergence) flagged below, and it confirms rather than resolves the underlying consistency concern — the data condition it guards against can still exist in your store.
Key questions. Which storage backend is the actual target? Who owns the PBJ schema repository and its review process? Which Laravel and PHP versions are validated, and by whom? Is there a commercial support or escalation path with the vendor? What is the migration story for existing content tables? What are the read/write latency and hydration characteristics at expected node counts? Does the team have any event-sourcing experience, or is this their first? What produces an "aggregate-synced node with an empty event stream" in our design, and what is our repair path now that the read handler rejects them?
Stack fit. Best treated as an isolated domain module inside a conventional Laravel app: NCR owns content nodes, Eloquent/DB remains for everything else. gdbots/ncr-eloquent uses Laravel's database layer, so connection pooling, transactions, and migrations stay familiar. Queue workers are the natural host for PBJX event dispatch and curation transitions; Artisan commands wrap schema compilation and reindex jobs.
Migration path. Start with the in-memory repository to get the domain model and tests right without infrastructure. Model two or three representative content types in PBJ, compile, and wire a read-only repository against a staging database. Run shadow reads against existing tables to validate node hydration and slug/reference behavior, and explicitly query for nodes whose aggregate has no events — post-v3.1.2 those reads now fail rather than return a hollow node, so surface the count as a migration gate. Then enable the write path (create/update/publish/delete) behind a feature flag, per content type, and retire legacy tables only after a full publish-cycle bake period.
Compatibility. Pin exact versions of every gdbots/* package and treat upgrades as deliberate change events, not routine composer update. v3.1.2 changes handler behavior, not public API, so it is a safe pull-in — but pin it deliberately and re-run the shadow-read reconciliation, because the failure mode it addresses will now present as errors instead of silently-returned nodes. Verify illuminate/database and Symfony event-dispatcher version overlap early — collisions here are the most likely first blocker. Confirm extension requirements (ext-json is certain; ext-protobuf may or may not be required depending on PBJ configuration) against the deployment image before committing.
Sequencing. (1) Two-week spike: install, model one content type, prove read/write against the target driver, document friction. (2) Service provider, config, and container bindings. (3) Read-only vertical slice in staging, including an empty-event-stream reconciliation query. (4) Write path plus event bus and queue workers. (5) Curation/publishing workflow and cache invalidation. (6) Search/index integration. (7) Per-type production cutover. Do not compress steps 1–2; they are where the go/no-go signal lives.
Maintenance. The team owns all glue code permanently. Expect to read package source regularly, because error messages from deep inside PBJX will not be self-explanatory. Release cadence is low and patch-oriented — v3.1.2 is a single-handler fix — which cuts both ways: few breaking changes, but also limited signal that defects found in production will be triaged on a predictable timeline. The upside is evidence the vendor does still patch correctness bugs in the read path; the downside is that the fix arrived only after such a state could be produced.
Support. There is no community to lean on. Either negotiate a support arrangement with the vendor, budget internal time to fork and maintain under Apache-2.0, or accept that the first serious production bug becomes an unbounded investigation. For a TPM, this is the single largest line item in the risk register and should be priced explicitly.
Scaling. Entirely driver-dependent. DynamoDB gives horizontal scale but locks you to AWS and its cost model. The Eloquent path scales with your relational database and is sensitive to node hydration cost and N+1 patterns when resolving NodeRef graphs — plan for a cache layer (Redis) in front of hot nodes and for eager-loading discipline. Event volume on publish-heavy workloads will drive queue sizing.
Failure modes to design for now. Schema compile drift between environments; partial writes where the node row commits but the event does not — v3.1.2 converts this from a silent wrong-answer into a request-level error, which is better but not self-healing, so you still need a reconciliation job and an alert on the error signature; cache invalidation on publish/unpublish/delete; slug uniqueness races under concurrency; optimistic-concurrency (etag) conflicts surfacing as raw errors to editors; and queue backpressure stalling curation transitions. Each needs an explicit mitigation and an alert.
Ramp-up. Realistically two to four weeks for a senior PHP engineer to reach productive velocity, with a named owner thereafter. The learning curve is conceptual — schema-first types, node references, event-sourced writes, aggregate sync — not syntactic. Budget for that owner to write internal documentation, since upstream will not provide it.
TPM recommendation. Treat this as a build-vs-buy decision with an unusual shape: you are buying a domain model, not a supported product. Fund the two-week spike with written exit criteria (working read/write on the target driver, acceptable latency at projected volume, no unresolvable version conflict with the current Laravel major, and zero unexplained empty-event-stream nodes in staging). If the spike passes, adopt narrowly and isolate it, pinning v3.1.2 or later. If it does not, the conventional Laravel/Eloquent path with a hand-rolled curation status field will deliver most of the business value at a fraction of the operational risk.
How can I help you explore Laravel packages today?