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
| Entity | Table | Key fields |
|---|---|---|
SubscriptionPackage | Saas.SubscriptionPackages | Name/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 |
SubscriptionPlan | Saas.SubscriptionPlans | Name/FName, PaymentPlan (enum: Monthly=1, Annually=2), IsDefault |
SubscriptionPlanPackage | Saas.SubscriptionPlanPackages | PlanId, PackageId, Price — this 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 |
SubscriptionFeature | Saas.SubscriptionFeatures | Key (e.g. "Features.ProductsManagement"), ModuleKey, IsLimited, Price |
SubscriptionPackageFeature | Saas.SubscriptionPackageFeatures | PackageId, FeatureId, MappingType (enum: NeedsUpgrade=0, WithIn=1, ExtraCost=2) — the package↔feature join row |
SubscriptionFeatureSettingsGroup | Saas.SubscriptionFeatureSettingsGroups | SubscriptionFeatureId, SettingsGroupKey — feature→settings-group link |
SubscriptionPackageFeatureRule | Saas.SubscriptionPackageFeatureRules | FeatureId (→ SubscriptionPackageFeature.Id, not SubscriptionFeature.Id), RowCounter, EntityName, ApplyPeriod (enum: WhenSubscribing=0, Daily=1, Monthly=2, Annual=3) — see Chapter 7 |
SubscriptionFeatureReportAction | Saas.SubscriptionFeatureReportActions | SubscriptionFeatureId, AppReportNameActionId — feature→report-action link |
AppReportNameAction | Saas.AppReportNameActions | ActionKey (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.
