Skip to content

Epic: Conjoined multi-tenancy for EF Core with Weasel-managed tenant partitioning #3465

Description

@jeremydmiller

Tracking issue for the conjoined EF Core multi-tenancy epic: ITenanted on an EF entity makes it conjoined-multi-tenant through Wolverine — mapped tenant_id, tenant-bound global query filter, stamp-on-insert, cross-tenant write rejection, opt-in Weasel-managed physical partitioning, and CritterWatch tenant management out of the box. Behaviorally compliant with what Marten (and Polecat) do for conjoined tenancy today.

Motivating context: https://barretblake.dev/posts/development/2026/07/multi-tenant-part-1/ — hand-rolled shared-database tenancy in EF Core 10 (tenant column, named query filters everyone must remember, raw partition DDL smuggled into EF migrations, "one forgotten IgnoreQueryFilters() and Party A is reading Party B's mail"). Every one of those pain points is something the critter stack already automates for Marten; this epic brings it to EF Core.

Decisions

  1. The ITenanted marker is shared critter-stack-wide from JasperFx.MultiTenancy (Marten/Polecat re-point their identical local markers).
  2. SQL Server gets physical partitioning in v1 via Weasel.SqlServer.ManagedTenantPartitions; a follow-up epic brings Polecat to full Marten per-tenant-partitioning parity.
  3. Tenant lifecycle (enable/disable, authoritative tenant list) lives in a Wolverine-owned wolverine_tenants registry table, not in Weasel's partition control tables.
  4. Conjoined sagas are in scope.

Work items

Upstream prerequisites

Wolverine phases

Companions / follow-ups

Sequencing

jasperfx#531 (ITenanted)  ─┐
weasel#362  (SqlSvr gaps) ─┼─> Marten/Polecat pin bumps
                           │
#3462 Phase 1 ─────────────┤  needs jasperfx#531 only
#3463 Phase 2 ─────────────┤  PG ready now; SqlServer needs weasel#362
#3464 Phase 3 ─────────────┤  needs Phase 1; CritterWatch#720 in parallel
Phase 4 compliance/docs ───┤
                           │
polecat#335 parity epic ───┘  needs weasel#362; parallel to Phases 2–4

Verify-early risks

  • EF FindAsync vs global query filters (saga load correctness) — spike first.
  • EF filter caching vs per-context tenant state; interplay with EF 10 named filters.
  • Phase 2 composite-PK rewrite changes keyed-load call shapes for user code — needs loud docs/diagnostics.
  • SQL Server ordinal stamping needs a synchronous-safe, pre-hydrated lookup (Polecat TenantEventSequenceRegistry pattern).
  • Marten conjoined + EF conjoined in one database must coexist (mt_tenant_partitions vs wolverine_tenant_partitions; CritterWatch fans out to both sources by design).

Open naming/API questions

  1. Registration API: AddDbContextWithWolverineManagedConjoinedTenancy<T> vs something shorter (AddDbContextWithConjoinedTenancy<T>?).
  2. Table names wolverine_tenants / wolverine_tenant_partitions in the durability schema.
  3. Partitioned sagas: globally-unique saga ids vs per-tenant id reuse (composite identity).
  4. Cross-tenant write exception naming.
  5. Plain conjoined (no partitioning) stays EF-migrations-friendly, or also requires Wolverine-managed migrations?

Full analysis and phase detail: CONJOINED-TENANCY-EPIC-PLAN.md (repo root, working doc).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions