ข้ามไปยังเนื้อหา

Tutorial: Multi-tenant SaaS

เนื้อหานี้ยังไม่ได้แปลเป็นภาษาไทย แสดงเป็นภาษาอังกฤษแทน

Multi-tenancy is one of those decisions that’s painful to retrofit but trivial when designed in. CephalonEngine ships first-party multi-tenancy through Cephalon.MultiTenancy and Cephalon.MultiTenancy.Governance — runtime-neutral, host-agnostic, and built to handle the full governance surface (memberships, invitations, declared domain ownership, approval/remediation workflows).

  • a tenant-aware version of the First-app modular monolith.
  • tenant resolution from a subdomain, header, or JWT claim.
  • ambient ITenantContext available across modules.
  • durable tenant-membership and tenant-invitation governance.
  • declared domain ownership with verification + remediation flows.
  • per-tenant data isolation through a shared DbContext with row-level filtering.
  • Cephalon.MultiTenancy — host-agnostic tenancy primitives. M2.
  • Cephalon.MultiTenancy.AspNetCore — ASP.NET Core resolver middleware. M2.
  • Cephalon.MultiTenancy.Governance — durable governance storage. M2.
  • Email-delivery adapters: Amazon SES, Mailgun, Microsoft Graph, SendGrid, SMTP. All M2.
Program.cs
builder.Services
.AddCephalonAspNetCore()
.AddMultiTenancy(options =>
{
options.Resolvers.UseSubdomain();
options.Resolvers.UseHeader("X-Tenant");
options.Resolvers.UseClaim("tenant_id");
})
.AddTenantGovernance(options =>
{
options.UseEntityFramework<TenantGovernanceDbContext>();
options.EmailDelivery.UseSendGrid();
})
.AddModulesFromAssemblies(/* ... */);
// Module
public sealed class ProductsModule : RestBehaviorModuleBase
{
public override ModuleDescriptor Describe() => new(
name: "Acme.Store.Modules.Products",
version: "1.0.0",
capabilities: [Capability.Data, Capability.Tenancy]); // declares tenancy
public override void RegisterServices(IServiceCollection services)
{
services.AddCephalonEntityFramework<ProductsDbContext>((sp, options) =>
{
var tenant = sp.GetRequiredService<ITenantContext>();
var conn = sp.GetRequiredService<ITenantConnectionResolver>().ResolveProducts(tenant);
options.UseNpgsql(conn);
});
}
}

The full walkthrough is part of the upcoming v0.2.0-preview docs push. Until then: