andydefer/laravel-chronos
Laravel Chronos is an advanced scheduling engine for Laravel. Manage recurring availability, appointments and blocked periods, prevent double bookings, enforce 17 business validation rules and automatically find the next free slot.
Architecture fit. Chronos is a domain-rich scheduling engine rather than a thin CRUD helper. It ships a layered design: service contracts (AvailabilityServiceInterface, ScheduleServiceInterface, SlotServiceInterface, ImpedimentServiceInterface), immutable-ish input DTOs (AvailabilityRecord, ScheduleRecord, ImpedimentRecord), typed collections (SlotVOCollection, BlockedPeriodCollection), enums (ScheduleStatus), and value objects (DateTimeZuluVO). That maps well onto a Laravel codebase already using interface binding and a service layer, and poorly onto one where Eloquent models are used directly in controllers. The ->for($owner) scoping convention is the most consequential design decision: it implies a polymorphic owner (doctor, room, resource, tenant) threaded through every read and write. If our models already carry a tenant or organization key, we need to confirm Chronos's owner concept can be aliased to it rather than introducing a second, parallel ownership axis.
Integration feasibility. Straightforward mechanically: composer require, two vendor:publish tags, migrate. The package publishes its own schema, so the real work is data modeling, not wiring. The README's booking example is a check-then-create sequence (findNextSlot → isSlotAvailable → create), which is a textbook time-of-check/time-of-use gap. Before committing, we must establish whether uniqueness is enforced by a database constraint or only by the 17 application-level validation rules — if the latter, we own the race condition and must wrap booking in a transaction with row locking or a unique index of our own.
Technical risk. High, concentrated in provenance rather than code quality. Zero stars, zero dependents, no downstream production signal, and a single apparent maintainer means no blast-radius evidence. Documentation is French-only and the provided excerpt is truncated mid-file, so we cannot see the full rule set, the config surface, or the migration schema without cloning the repo. The advertised "17 validation rules" and "infinite loop protection" are the kind of features that are easy to claim and hard to get right at DST boundaries, overlapping availability windows, and partial impediment overlaps — all of which the API explicitly models (getFullyBlockedSchedules vs getPartiallyBlockedSchedules). DateTimeZuluVO signals UTC-only storage, which is correct, but pushes local-timezone rendering and DST-correct slot generation into our application layer.
Key questions.
->for($owner) enforced on every read path, or is it a convenience filter that a direct model query can bypass?findNextSlot bound its search — hard iteration cap, date horizon, configurable lookahead?Stack fit. Laravel 10/11/12 and PHP 8.1+ aligns with a current LTS-track application; if we are on Laravel 9 or PHP 8.0, this package is a non-starter without a fork. The interface-first design is the strongest argument for adoption: because we depend on *ServiceInterface rather than concrete classes, we can bind Chronos implementations in a service provider and swap them later. The recommended pattern is an anti-corruption layer — our own BookingService/AvailabilityService facades that call Chronos contracts internally — so that no controller or job references AndyDefer\LaravelChronos\* directly. This caps the cost of abandonment at the adapter, not the feature.
Migration path. Two distinct cases. Greenfield scheduling: adopt Chronos as the scheduling bounded context from day one, publish migrations, model owners via the for() scoping target, and write our own booking transaction wrapper. Existing scheduling data (a appointments table, calendar events, or a legacy availability table): do not attempt an in-place migration of the live table. Stand up Chronos alongside it, backfill availabilities and schedules via a one-off artisan command, dual-write during a shadow period, and reconcile counts per owner per week before cutting reads over. Recurring availability is the risky backfill — legacy systems often store one row per bookable slot rather than a recurrence rule, so we will need to infer days/daily_start/daily_end windows and accept that inference is lossy.
Compatibility. Three surfaces to validate before sequencing: (1) schema — table name collisions with existing tables, and whether FK cascades match our retention policy; (2) tenancy — whether for() composes with our global scopes or fights them; (3) timezone — confirm every consumer converts UTC to local at the boundary and that DST transition days are covered by tests we write, not tests we assume exist.
Sequencing.
Maintenance. Assume we will eventually own this code. Zero dependents and zero stars means no upstream bug-fixing gravity; a fork is the realistic end state. Budget for a named maintainer, an internal mirror, and a documented divergence policy. Interface-first design makes a later replacement cheap, but only if the anti-corruption layer is actually respected — the first direct use AndyDefer\...\Models\Schedule in a controller erodes that option.
Support. French-only documentation raises onboarding cost for a non-francophone team and, more importantly, raises the chance of misreading domain semantics (e.g., whether validity_end is inclusive). There is no visible issue history, security policy, or release cadence to reason about SLA. Treat vendor support as nonexistent; our team is tier 1 through tier 3.
Scaling. Slot generation is the hot path. findNextSlot must iterate availabilities across a date window, and cost grows with the number of availability records per owner and the lookahead horizon. Enforce a configurable horizon and cache computed slot sets per owner per day with invalidation on schedule/impediment mutation. Read-heavy calendar views should query Schedule directly through indexed range queries rather than recomputing slots. Watch index coverage on the polymorphic owner columns plus start/end timestamps — this is the single most likely production performance defect.
Failure modes.
Ramp-up. Engineering ramp is moderate: the DDD vocabulary (Records, VOs, typed collections) is idiomatic but adds a layer over plain Eloquent that mid-level developers will initially work around rather than through. Product/ops ramp is low — the domain model (availability, schedule, impediment) maps to how schedulers already think. Plan a half-day internal walkthrough, a written timezone/scoping convention, and a short "never touch Chronos models directly" rule in the contributing guide. Given the package's immaturity, the honest TPM recommendation is: worth a spike for the domain model alone, not worth adoption until our own correctness harness passes.
How can I help you explore Laravel packages today?