friends-of-ddd/sequence-number-generator
A Laravel/PHP sequence number generator for creating formatted, sequential identifiers such as invoice, order, and document numbers. Configure patterns to produce consistent, human-readable values across your application.
Architecture fit. As a TPM, the reason this package matters is not the code — it is that it gives us a named, owned place for a recurring business rule: "this document gets the next human-readable number." Invoice numbers, order references, ticket IDs, policy numbers, waybills, and contract IDs all share that shape, and today each feature team tends to solve it locally, which produces inconsistent formats across tenants, duplicated counter tables, and audit exposure. The "friends-of-ddd" framing implies an interface-driven core with a thin Laravel service provider — exactly the seam that lets the domain layer declare the need and infrastructure decide how it is satisfied (database table in production, in-memory fake in tests). That is a roadmap-level win: it turns "numbering" from a per-feature implementation detail into a reusable platform capability we can standardize on. The counter-caveat is that a port only has value if there is more than one adapter and a stable contract; if the package ships a single concrete implementation with an incidental API, we are adopting a utility, not an abstraction, and should plan to define our own interface on top.
Integration feasibility. Composer-installable, PSR-4 autoloaded, with conventional Laravel provider/config/facade wiring, so the mechanical integration is genuinely low-effort — likely under a day for a pilot. The delivery effort sits elsewhere and we own it regardless of vendor choice: choosing the backing store, deciding whether numbers must be gap-free or merely unique and ordered, defining scope keys (per tenant? per fiscal year? per branch?), and specifying the output format (INV/2025/000123, padding width, prefixes). Those are product decisions that need to be made before integration, and they are where the schedule risk actually lives — a team that installs the package before answering the format and reset questions will rework it.
Technical risk. The dominant risk is provenance, not quality: 0 stars, 0 dependents, and a near-zero composite score mean there is no field evidence that this runs in anyone else's production. We cannot infer test coverage, release cadence, or issue responsiveness from the signals available. Secondary risks we should consciously accept or mitigate: (1) gap-free semantics are the hardest part of this problem — true gapless numbering requires the counter allocation and the business write to commit atomically, plus a reconciliation story for rollbacks; a README bullet promising gap-free output is not the same as a documented transactional guarantee, and any audit or regulatory requirement must be scoped against verified behavior, not marketing copy; (2) concurrency behavior is unspecified — row locks, SELECT ... FOR UPDATE, Redis INCR, and advisory locks have materially different failure and performance profiles, and a local SQLite demo will pass while MySQL under parallel workers will not; (3) Laravel/PHP version drift — with no visible CI matrix a modern Laravel upgrade could orphan it; (4) formatting — the core likely yields an integer, meaning prefixes, padding, per-tenant formats, and check digits are our build, which quietly expands scope. Mitigating all four is cheap: pin the version, wrap it behind our own interface, and write a concurrency test before we commit.
Key questions for the maintainer/team to answer before commitment:
dev-main)?Stack fit. Strong on Laravel 9+/PHP 8.1+ with Composer and a container at the edges; weaker on legacy Laravel 6/7 or a codebase that lacks dependency injection at service boundaries. Before planning sprints I would confirm the declared version constraints, because "does it install" and "will it still install after our Q3 Laravel upgrade" are different roadmap items.
Migration path — the recommendation. Adopt this as a replaceable infrastructure detail, never as a domain dependency. The sequence for delivery:
SequenceNumberGenerator interface in the domain layer — even if it is a one-for-one copy of the package's contract. This is the insurance policy and costs a few dozen lines.Communicated properly, this converts "we are betting on an unknown library" into "we are choosing an implementation detail," which is a materially different risk conversation to have with architecture review and with executives.
Compatibility. Validate across our real matrix, not a laptop: PHP version, Laravel version, database driver (MySQL and PostgreSQL lock semantics diverge meaningfully), cache/queue driver, and any coexistence with existing ID strategies — auto-increment PKs, UUID/ULID, Snowflake — to ensure we are not creating a second, conflicting notion of "the order's number." Also confirm the output is treated as a business identifier alongside the immutable technical ID, rather than replacing it.
Sequencing (the plan I would actually schedule).
composer.json at that point, not before.Maintenance. We must assume we may become the maintainer — a small library extracted for reuse, not a supported product with a roadmap. Practical posture: pin the exact version, add a smoke test that exercises the public API so a routine composer update cannot silently break numbering, and (licensing permitting) vendor or fork it into our GitLab so an upstream disappearance is a non-event. Review it at each framework upgrade; a dormant package is harmless until the Laravel minor it targets leaves security support, at which point we either fork or replace.
Support. There is effectively no support channel to lean on. Our team is first-line support, so assign a named owner — realistically a backend lead — and treat the package as internal infrastructure with the same on-call expectations, not as a vendor dependency we can escalate.
Scaling. This is the sharpest product/engineering trade-off in the whole assessment: gap-tolerant numbering scales cheaply; gap-free numbering does not. A single counter row serializes writes per sequence, which is fine at invoice/order frequency (low hundreds per minute) and painful at event or event-tick frequency. Redis INCR scales better but can lose numbers on restart — which is precisely the behavior a finance or audit stakeholder will object to. The decision to make explicitly, with the finance/compliance stakeholder in the room, is: do we need strictly consecutive numbers, or do we only need unique and ordered? Answering that one question in advance determines the backend, the throughput ceiling, and whether we need to build gap detection. It should be captured in the ADR, not discovered in production.
Failure modes to plan for. Duplicate numbers from a concurrent allocation race (worst case — a compliance event, not a bug); gaps from a crash between allocation and commit; total inability to create invoices or orders when the sequence store is unavailable (a dependency that can halt core revenue workflows); and silent drift after a flush or stale-backup restore leaves the counter behind the max issued number. Mitigations: idempotency keys at the domain boundary, monitoring and alerting on duplicate-key violations and allocation latency, a documented reset/reconciliation runbook, and a startup guard that refuses to allocate below the highest number already issued. Each is a backlog item we should plan for now and not inherit at launch.
Ramp-up. Low for engineers — the API surface is small and the integration is a day's work at most — but non-trivial for the organization, because the hard parts are policy questions (tenant scoping, reset cycles, audit guarantees, formatting standards) rather than code. Budget a short design session with finance/compliance before the first production feature, and treat the pilot's ADR as the onboarding artifact: it tells the next team what is guaranteed, what is not, and how to extend without forking. If the pilot forces us to reimplement any of this ourselves, the fallback is well-bounded — a competent engineer can rebuild behind the same interface in about a day — which is ultimately why the adoption risk here is acceptable: the cost of being wrong is small, and the cost of standardizing is smaller.
How can I help you explore Laravel packages today?