Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tenanti Laravel Package

orchestra/tenanti

Orchestra Tenanti brings multi-tenancy to Laravel: register tenants, switch database connections per tenant, and run tenant-aware migrations and Artisan commands. Includes tenant resolvers, configurable drivers, and a simple API for building SaaS apps with isolated data.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. orchestra/tenanti is a narrow, high-leverage piece of infrastructure: it specializes Laravel's Migrator and DatabaseManager to run the standard migrate/rollback/seed command family against a named tenant connection, selected at runtime. This only fits if the tenancy decision has already been made in favor of database-per-tenant or schema-per-tenant rather than a shared schema with a tenant_id discriminator. That is the pivotal architectural fork, and it is a product decision as much as an engineering one — it determines onboarding cost, backup/restore design, data-residency claims, and the shape of every future migration for the life of the platform. Tenanti does not provide tenant-aware cache, filesystem, queues, sessions, routing, or a tenant registry. It assumes you bring a tenant model and a resolver, and it covers the error-prone schema plumbing around them. Evaluate it as the migration half of a tenancy stack, not the whole stack.

Integration feasibility. The integration surface is unusually small, which is the package's main technical virtue. Publish the config, define one class extending Orchestra\Tenanti\Tenant implementing getConnectionName() and getConfig(), register it under a driver key, point the driver at a dedicated migration directory, and a working proof-of-concept is a ten-minute spike: two tenants, one trivial migration, tenanti:migrate user --tenants=1,2 --pretend. If --pretend emits SQL against two distinct database names, the wiring is correct and the remaining work is orchestration, not integration. There is an observer (Orchestra\Tenanti\Observer) that hooks provisioning into tenant row creation, which is the right affordance for automated onboarding.

Technical risk. The dominant risk is framework coupling, not package quality. Because Tenanti extends internals, a Laravel major upgrade can silently change connection resolution or migration batch semantics. Mitigations are cheap but non-optional: pin exact versions (6.3.*, not a caret), commit the lockfile, and stand up a CI harness of two ephemeral tenant databases running migrate then rollback on every build. Budget roughly half a day per Laravel major upgrade for validation. Secondary risks are operational rather than integration-shaped: the default connection can still be used by queue workers, scheduled commands, and anything holding a stale connection instance, producing wrong-database writes (a data-integrity incident, not an availability one); connection instances accumulate in long-running workers and can exhaust max_connections; cross-tenant operations are not transactional, so a fan-out wave leaves partial state on failure; and there is no built-in locking, so concurrent deploys can collide on the same tenant. All of these have known, cheap mitigations — middleware chokepoint plus an assertion on active connection name, DB::purge() at job end, cache locks per tenant, re-run-oriented tooling — but they are work you own.

Key questions.

  • Which isolation model is committed: database-per-tenant, schema-per-tenant, or shared schema? If the answer is "not yet decided," this package is premature.
  • Which Laravel major is production, and does the pinned package version explicitly support it? The listing data does not establish support breadth; verify against composer.json before writing code.
  • Which database engine — MySQL or PostgreSQL? Schema-per-tenant semantics diverge; validate early.
  • What is the enterprise/compliance driver? If isolation is contractual, the per-tenant database becomes a defensible sales claim and changes the cost calculus favorably.
  • How large is the tenant base expected to be at 12 and 24 months? Fan-out time, connection counts, and backup surface scale linearly with tenants.
  • Who owns the fork if upstream stalls? Zero dependents and thin maintenance mean a fork contingency should be assumed, not hoped against.

Integration Approach

Stack fit. Native Laravel/PHP only. The package extends framework internals rather than reimplementing them, so it inherits Artisan ergonomics, the migration repository, and seeder behavior — meaning engineers already fluent in migrate are productive on tenanti:migrate immediately. Fit is excellent for a Laravel monolith moving to per-tenant isolation; poor for polyglot or non-Laravel services, and irrelevant if the isolation model is shared-schema.

Migration path. For greenfield tenants, the sequence is straightforward: define driver, create database, register connection, run tenanti:migrate. For converting an existing single-database install, do not replay historical migrations per tenant — one non-idempotent historical migration multiplied by N tenants is an outage. Instead baseline the current schema as "tenant v0," create tenant databases from that baseline, then insert the corresponding rows into each tenant's migrations table so Tenanti considers them current. Separately, keep control-plane migrations in database/migrations and tenant migrations in a dedicated per-driver directory; do not share files and filter with --path, because the first control-plane migration with a foreign key to users breaks tenant migrations. Note also that foreign keys cannot cross databases — relationships between tenant tables and the central users/tenants tables are enforced in application code, and require the consistency/cleanup job a real FK would have made unnecessary.

Compatibility. The critical compatibility axis is the Laravel major version, and the supplied material does not document the supported range. Treat support breadth as unverified until confirmed against the package's composer.json and CI matrix — that is the single most common early failure mode. A second axis is database engine: schema-per-tenant behavior differs between MySQL and PostgreSQL, so pick the target engine before committing. A third is organizational: any code path touching a tenant table without an explicit connection target is a latent bug, so compatibility is as much about team conventions as version numbers.

Sequencing.

  1. Confirm the isolation architecture decision and write it as an ADR; it is a pricing/packaging input, not just a dependency choice.
  2. Spike: pinned install, published config, one driver, two tenants, --pretend verification. Timebox to a day.
  3. Wrap the package behind an internal service class (TenantSchema) exposing migrate, rollback, seed, schemaVersion. Application code talks to that, never to Orchestra\Tenanti\*. This is the single highest-value structural decision — it converts a fork contingency from a codebase-wide search into a one-class swap.
  4. Build the tenant registry and resolver, then wire the Observer into an explicit onboarding action so provisioning is automated and emits a completion event rather than merely a creation.
  5. Ship the CI harness (two ephemeral tenant DBs, migrate + rollback) before the first production fan-out.
  6. Add lock-based fan-out orchestration and per-tenant result logging, starting with a single deploy lock, then moving to per-tenant cache locks.
  7. Only then expose staged rollouts and canary cohorts as a product capability.

Operational Impact

Maintenance. This is the primary cost. With zero dependents, no meaningful star traction, and a narrow ecosystem, the package should be treated as a potential maintenance liability rather than a strategic dependency. The source is roughly two thousand lines and readable in an afternoon; that investment is mandatory, because questions like "what exactly happens if a tenant migration fails midway" are answered only in code. Version pinning plus the CI upgrade harness converts the recurring cost into a predictable half-day per Laravel major. The wrapper class caps the blast radius if you must vendor or fork. There is no documented support matrix in the supplied material — assume you are your own first line of support.

Support. Upstream responsiveness is unproven from the available signals (score 0.3, no dependents). Plan for self-support: a named owner for the tenancy layer, an internal runbook covering fan-out, rollback, drift reconciliation, and canary promotion, and a written fork/swap contingency. The wrapper class is what makes that contingency credible.

Scaling. Costs scale linearly and in three distinct dimensions. Migration time grows with tenant count, which is why chunked fan-out in waves of ~25 with per-tenant success/failure logging is the operating norm rather than a nicety. Connection count grows with concurrent tenant resolution, and long-running queue workers are the sharp edge — each resolved tenant adds a database-manager entry and an open connection, so DB::purge($connectionName) at job end plus capped worker concurrency is required to avoid exhausting max_connections overnight. Backup and restore surface grows one-for-one with tenant databases, which is a real platform cost that should be reflected in pricing and support tiers. This profile is a strong fit for mid-market and enterprise segments where isolation is contractual, and a poor fit for high-volume low-ACV self-serve, where per-tenant databases multiply cost faster than they add value.

Failure modes.

  • Wrong-database writes from queue workers, scheduled commands, or stale connection instances. Highest severity — data integrity, not availability. Mitigate with a single chokepoint (middleware/job middleware that sets and tears down tenant context) plus an assertion at the top of every tenant job that the active connection name matches the expected tenant. Any path touching tenant tables without that assertion is a bug.
  • Partial fan-out from mid-wave failure, with no distributed transaction to save you. Design every operation for re-run rather than atomicity; migrations are idempotent from the repository's perspective, so failed tenants can be re-run alone.
  • Concurrent deploy collision on the same tenant. Ship the coarse single-deploy lock first; refine to per-tenant cache locks later.
  • Drift between tenants. The package provides no reconciliation; per-tenant migrations tables are your only visibility, so build the schema-version query into a health check from day one.
  • Silent framework upgrade breakage in connection resolution or batch semantics. Caught only by the CI harness — another reason it precedes the first production fan-out.
  • Forgotten --force / --no-interaction hanging a production pipeline on an invisible prompt.

Ramp-up. Fast for the happy path, slower for the failure paths. A Laravel-fluent engineer reaches a working two-tenant spike in under an hour following the published config and one driver class. Realistic time-to-proficiency is two to three days to internalize the connection-isolation discipline, the fan-out workflow (canary with --pretend, canary for real, verify, chunk the remainder, re-run failures), and the drift baseline query; call it one to two weeks before an engineer can be trusted to run a production fan-out unsupervised. The published config is effectively the entire public surface area, so most of the ramp is operational judgment rather than API surface — which is a good sign for the package and a reminder that the risk sits in your orchestration layer, not in the dependency.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource