jetcod/eloquent-keygen
Laravel package that integrates Snowflake ID generation with Eloquent models, giving you automatic, distributed, unique primary keys via a base model or trait, with per-model opt-out and configurable settings for scalable applications.
Architecture fit. This is not an ID-generation library — the algorithm lives in godruoyi/php-snowflake. jetcod/eloquent-keygen is a thin Eloquent adapter: a trait (plus a base model that uses it), a service provider, and a config file that populates the primary key on the creating event. That shapes the decision correctly: what we would be buying is a convention, not capability. The capability is ~a trait's worth of glue we could own in half a day. Adopt it only if we want one centrally-decided ID convention rather than three teams rolling their own.
Architecturally it fits when the ID strategy is still open: greenfield tables, a service being split out, or a roadmap item involving multi-region writes, sharding, or handing IDs to queue workers before commit. It does not fit where the database already guarantees uniqueness and no sharding plan exists — there we would take on a schema migration and node-ID operations for aesthetic benefit.
Integration feasibility. Mechanically trivial: composer require, publish config, then either extend Jetcod\Eloquent\Model or use Snowflake on the model. The per-model snowflake(): bool escape hatch means adoption is reversible per model. Cost of trying is days. The hidden work is not integration, it is schema and node identity: the id column must be BIGINT UNSIGNED (MySQL), every foreign key referencing it must change type, and worker/datacenter values must be uniquely allocated per running instance. The README's php artisan vendor:publish --provider="eloquent-key-generator-config" is almost certainly wrong and must be verified against the service provider in source before it reaches a runbook.
Technical risk.
php-snowflake ≥3.2, a string return type on PrimaryKeyGenerator::generate(), and generation moved from saving to creating. Small packages on this trajectory break consumers; we need version pinning and a contract test around ID generation.Number.MAX_SAFE_INTEGER, so API contracts must serialize them as strings, and any consumer parsing them as numbers will silently corrupt them.Key questions.
worker_id / datacenter_id allocation, and what is the mechanism (env injection, init container, service registry)? Platform/ops, not app teams.LAST_INSERT_ID() round-trips or auto-increment contention — or is this architectural preference? Get the number.LaravelSequenceResolver puts cache/Redis in the create path; FileLockResolver puts a filesystem path there.Stack fit. Laravel 10–13, PHP 8.1–8.5 (64-bit), php-snowflake ≥3.2. Verify our actual runtime matrix — including CI and local Docker images — before committing to a date. MySQL needs BIGINT UNSIGNED; PostgreSQL's signed BIGINT accommodates Snowflake values without change. SQLite test environments need the column type audited too.
Recommended shape: trait, not base class. use Jetcod\Eloquent\Traits\Snowflake on selected models keeps a third-party base class out of our model hierarchy. It also makes the exit path a copy-paste, and lets us stage rollout model by model with snowflake(): bool as the per-model kill switch. Mandate the trait; treat Jetcod\Eloquent\Model as unsupported in our standards doc.
Migration path. Three tiers, in ascending cost:
BIGINT UNSIGNED, aligned FK types, string serialization in API resources) applied up front.Compatibility checklist before install reaches a runbook. Confirm the real vendor:publish provider string; pin jetcod/eloquent-keygen and godruoyi/php-snowflake to exact versions; verify no packages assume integer/incrementing keys on the affected models ($incrementing, $keyType, route model binding, factory behavior); confirm bulk paths. insert(), upsert(), raw DB::insert, and Model::withoutEvents() all bypass the creating event and will write rows with no generated ID — imports and seeders must go through Eloquent or set IDs explicitly.
Sequencing.
php-snowflake bump.Maintenance. Small surface, small package, pinned versions: maintenance cost is low in absolute terms. The recurring obligation is watching upstream — v1.2.0 shows the maintainer will take breaking swings — plus re-running an ID-generation contract test on every dependency bump. Budget a named owner for the pin file and the ADR, or it silently drifts.
Support. No vendor. Single maintainer, GitHub issues, no disclosed security process. That is acceptable for a replaceable utility and unacceptable for anything on a compliance path. If procurement requires audited vendors, write the trait. Internal support burden is real but concentrated in one place: the node-ID allocation runbook, which must exist before the first production deploy and must cover pod restarts, autoscaling, and ephemeral containers.
Scaling. Snowflake's node budget is the binding constraint, not throughput: 5 bits datacenter + 5 bits worker gives 32 × 32 = 1,024 concurrent generators, with 4,096 IDs per millisecond per node. That is generous for typical fleets and tight if our deployment model is "one identity per pod" with aggressive horizontal autoscaling — in which case allocate worker IDs from a pool with lease/release, not statically. The sequence resolver is the throughput and availability ceiling per node: LaravelSequenceResolver makes Redis/cache a synchronous dependency of every INSERT, and FileLockResolver ties correctness to a per-node filesystem path.
Failure modes to instrument for.
saving to creating in v1.2.0; behavior changes are invisible until they corrupt something. Contract test is the control.Ramp-up. Engineering learning curve is an afternoon. The real ramp is organizational: platform/ops must own node-ID allocation, app teams must know the "don't bypass Eloquent" rule, and API consumers must know IDs are strings. Estimate the pilot at roughly two weeks including load validation and the runbook, with explicit exit criteria: no collisions across scaled replicas, write-path latency measured before and after, and a working per-model revert. Position it to stakeholders as contained plumbing with a half-day fallback to in-house code — not a platform bet, and never an architecture that only works because this package exists.
How can I help you explore Laravel packages today?