Skip to main content
Version: Latest

3. Architecture — Real Entity and Table Names

All of the entities below live in Shumoul.Framework.MultiTenancy.Api.Entities (or Entities.Reports), schema Saas, registered on TenantDbContext — the SaaS control-plane database (root tenant's own database; not per-customer-tenant data). There is a duplicate, dead legacy entity set under Shumoul.Domain.Entities.Tenants.MultiTenancy.Subscriptions (BackEnd repo) with no DbSet, no EF configuration, and no migrations anywhere — ignore it; every real seeder uses the Shumoul.Framework.MultiTenancy.Api.Entities types.

3.1 Core entities

EntityTableKey fields
SubscriptionPackageSaas.SubscriptionPackagesName/FName, MonthlyPrice, TrialDayCount, PackageLevel (enum: Trial=0, Basic=1, Professional=2, Enterprise=3), IsActive, DisplayOrder (from BaseEntity) — no Code/Key column; FName is the only stable text identity
SubscriptionPlanSaas.SubscriptionPlansName/FName, PaymentPlan (enum: Monthly=1, Annually=2), IsDefault
SubscriptionPlanPackageSaas.SubscriptionPlanPackagesPlanId, PackageId, Pricethis row's own Id is RegisterNewTenantRequest.PlanPackageId, and its existence is what makes a package appear at all on GET api/Subscription/Packages/pricing for a given plan
SubscriptionFeatureSaas.SubscriptionFeaturesKey (e.g. "Features.ProductsManagement"), ModuleKey, IsLimited, Price
SubscriptionPackageFeatureSaas.SubscriptionPackageFeaturesPackageId, FeatureId, MappingType (enum: NeedsUpgrade=0, WithIn=1, ExtraCost=2) — the package↔feature join row
SubscriptionFeatureSettingsGroupSaas.SubscriptionFeatureSettingsGroupsSubscriptionFeatureId, SettingsGroupKey — feature→settings-group link
SubscriptionPackageFeatureRuleSaas.SubscriptionPackageFeatureRulesFeatureId (→ SubscriptionPackageFeature.Id, not SubscriptionFeature.Id), RowCounter, EntityName, ApplyPeriod (enum: WhenSubscribing=0, Daily=1, Monthly=2, Annual=3) — see Chapter 7
SubscriptionFeatureReportActionSaas.SubscriptionFeatureReportActionsSubscriptionFeatureId, AppReportNameActionId — feature→report-action link
AppReportNameActionSaas.AppReportNameActionsActionKey (derived: Permission.{SystemFName}.{MenuFName}.{ReportFName}.{ActionKey}, spaces stripped) — see Chapter 8

3.2 The pricing endpoint

GET api/Subscription/Packages/pricing [AllowAnonymous]

SubscriptionPackageService.GetSubscriptionPricingAsync() builds one entry per active SubscriptionPlan, nesting one SubscriptionPackagePricingDetailsDto per SubscriptionPlanPackage row linking that plan to an active SubscriptionPackage, nesting one PackageFeatureDto per active SubscriptionPackageFeature, nesting FeatureRulePricingDto entries for any active SubscriptionPackageFeatureRule whose FeatureId matches that package-feature join row's own Id. This is why every package limit in this guide is documented against the specific feature it is attached to — that is also how it appears on the public pricing page.

3.3 Tenant → package resolution

TenantSubscription (TenantId, Status=Active, PackageId → really SubscriptionPlanPackage.Id)
→ SubscriptionPlanPackage.PackageId → SubscriptionPackage
→ SubscriptionPackageFeature → SubscriptionFeature
→ SubscriptionFeatureSettingsGroup (settings groups)
→ SubscriptionFeatureReportAction (report actions)

Resolved live, on every call, via ITenantFeatureService.TenantFeaturesIDs(tenantId) — no cached per-tenant entitlement snapshot exists. Editing a SubscriptionPackageFeature/ SubscriptionFeatureSettingsGroup/SubscriptionFeatureReportAction row takes effect immediately for any tenant whose active TenantSubscription resolves to that package.