Skip to content

Architecture

TenantGuard reads the model an app already builds and never changes it. Three packages work at three moments: the analyzers when the code compiles, the runtime package when EF Core builds the model and on every SaveChanges, and the test kit in the app’s tests. A fourth, the validate tool, runs the model rules in CI without a database, and two test adapters make each probe row a test of its own in xUnit v3 or TUnit.

  • Packages: the six packages, what each depends on, their namespaces, the rings inside the runtime package, the public API, and the repository’s layout.
  • Flows: what happens at build, when the model is built, on SaveChanges, and when the isolation probe runs.
  • Decisions: why each of these is the way it is.
  • Tenant property: the property that holds the tenant key on an entity, CLR or shadow, found as the rules reference describes.
  • Tenant-scoped entity: an entity type whose root type has a tenant property.
  • Shared entity: an entity type declared tenant-free on purpose, such as a lookup table (Shared<T>()).
  • Current tenant: the value the app says the context operates as, from the CurrentTenant resolver.
  • Guard: the write guard (the SaveChanges interceptor) and the model rules together.
  • Model validation adds under 50 ms to model building for a 200-entity model.
  • The write guard adds no allocations on a SaveChanges when the model has no tenant-scoped entity type, nothing is tracked, or a bypass is open, and O(entries) otherwise (0013).
  • Every message is deterministic and stable: messages are part of the public contract (Compatibility).
  • No reflection over user types at query time: per-model metadata is computed once and cached on the IModel.