orchestra/notifier
Orchestra Notifier is a Laravel package that provides a centralized notification system, letting you send, store, and manage user notifications across multiple channels through an extensible, database-backed architecture.
Architecture fit. orchestra/notifier is not a general-purpose Laravel package — it is a component of Orchestra Platform, and its value is proportional to how much of that platform you already run. It layers user-facing notifications (persistence, read/unread state, in-app plus mail channels) onto Orchestra's User and ACL components. That is a coherent design inside its host ecosystem and an awkward fit outside it: recipient resolution, authorization, and identity all presuppose Orchestra's user model. For a TPM the framing is simple: this is a platform component, not a library. Adopting it is a statement about your application's foundation, not a feature decision.
Integration feasibility. Mechanically low-effort, strategically constrained. It is a standard Laravel package: service provider, publishable config and migrations, a fluent dispatch API. The honest comparison is not "package vs. build from scratch" — Laravel ships notifications, channels, queues, and database persistence natively. The comparison is framework primitive plus a thin internal convention versus a platform-specific wrapper around the same primitive. The wrapper buys you Orchestra-native recipient/ACL plumbing and an opinionated inbox. It costs you portability and a dependency with no forward release stream. Feasibility is high; desirability is the open question.
Technical risk. Three items dominate, in order:
illuminate/* down to 8.x, taking the rest of the application with it. Every subsequent framework upgrade becomes blocked on a fork or a removal. This is the budget line item, not a footnote.notifications table with a notifiable_type/notifiable_id/read_at/data shape. If this package writes the same table name with a different schema, the failure is silent — a skipped migration because the table exists, or native writes landing in mismatched columns. This must be verified by reading the package's migrations before anything runs in a shared environment.Illuminate\Notifications.Key questions for the team.
notifications table already in use by native Laravel, and under what schema?Stack fit. Fits cleanly only if the stack is Orchestra Platform on Laravel 8. Outside that, it is a square peg: the package's recipient and ACL assumptions won't map onto a plain Illuminate\Foundation\Auth\User, and you'd be writing more glue than the package saves. The decision rule is binary — if you're not already on Orchestra, don't start here.
Migration path. For an inherited install, the path is extraction, not extension. Treat the package as archaeology: it defines your current notification surface area and tells you precisely what must be reimplemented — templates, recipient resolution, transports, inbox and read-state semantics — when you move to a supported Laravel line. The migration sequence:
App\Contracts\NotificationDispatcher with the two or three methods the product actually needs (notifyUser(), notifyAdmins(), markRead()). Write it before any call site imports Orchestra\Notifier\*.OrchestraNotificationDispatcher wrapping the package, and LaravelNotificationDispatcher built on native primitives. Same contract, same test suite. This is the insurance policy, and it is written in the same sprint as the package adapter — not "later," because later is when the upgrade is already on fire.If instead you are pinning to Laravel 8 deliberately and maintaining, the path is contain-and-pin: exact version, committed lock file, and no new call sites outside the adapter.
Compatibility. Pin exactly ("6.0.0", not ^6.0) and commit the lock. Run composer audit in CI with the explicit understanding that a CVE here has no upstream remediation — your remediation is the adapter swap, which is why step 3 above is not optional polish.
Sequencing. Adapter contract → package-backed implementation behind a feature flag → native-backed implementation → canary one low-blast-radius type (internal admin announcement, database channel only) → compare payloads against the existing path for a week → broaden channels → retire the package. Mail goes last: deliverability, templating, and queue behaviour add variables that obscure whether recipient resolution and persistence actually work.
Maintenance. The realistic failure mode is not a dramatic break — it is "no one noticed it stopped working" on a customer-visible surface. With no active release stream and effectively a single inactive maintainer, maintenance is entirely yours: you read vendor/ source because there is no issue tracker to search. Budget this as a recurring line item at each framework upgrade planning cycle, with a named person against it.
Support. Support posture is self-support with source access. That is acceptable for an internal admin tool and unacceptable for a customer-facing notification path unless the adapter swap is already written and tested. Extension points you can rely on: binding your own channel driver (if the driver interface is stable), decorating the dispatcher for logging/retry/metrics, and overriding views through your own namespace. Anything requiring an edit to vendor/ is a fork — decide that consciously rather than discovering it during an incident.
Scaling. Follow the standard shape regardless of package:
$tries and $backoff on the listener; defaults are too aggressive for a channel you don't control.(notifiable_id, read_at) and (notifiable_id, created_at). The unread badge is the hottest query in any inbox UI and a table scan without the first index.Failure modes to instrument for.
migrate "succeeds," no table appears.notifications name.ModelNotFoundException or a silent no-op. Decide deliberately whether that fails the job or discards.config:cache freezes published config; make cache-clear part of the deploy script.Ramp-up. Low for a developer already fluent in Laravel notifications — the API is idiomatic. The ramp-up cost is not learning the package, it is learning the absence of a support channel: debugging is an ordered manual ritual (did the event fire → did the job queue → did it land in failed_jobs → did the adapter reach the channel → did the package do anything). Document that checklist, document the correlation ID convention (event ID + recipient ID logged at dispatch), and the on-call burden drops materially. Plan on roughly a sprint to stand up the contract, both adapters, and the canary — and treat that sprint as the real cost of the decision, whichever way it goes.
How can I help you explore Laravel packages today?