4. Architecture
4.1 Two-repository split
This feature deliberately spans two repositories, each responsible for the part of the system it already owns — no data or logic is duplicated between them.
Shumoul.MultiTenancyApi (Shumoul.Framework.MultiTenancy.Api / .Host)
The central, cross-tenant SaaS host. Responsible for:
- Survey definitions, questions, profiles, and recommendation rules
- Tenant onboarding sessions (one per tenant per survey)
- Recommendation generation (rules engine over the tenant's answers)
- Settings patch generation (the JSON patch describing what should change)
- Apply logs (audit trail of what was applied and by whom)
- The public
api/v1/onboarding/*endpoints Angular calls
Shumoul.BackEnd (Shumoul.Api / Shumoul.Infrastructure / Shumoul.Application)
The tenant-facing ERP host. Responsible for:
- Real
AppSettingspersistence, per tenant, per the tenant's own resolved database IAppSettingService— the one official settings read/write service every other ERP feature already uses- Tenant resolution via
ApplicationDbContext(each tenant's own database connection) - Actually applying a settings patch through
IAppSettingService, including automatic cache invalidation (SetAppSetting<T>()already clears the relevant cache — no separate step needed) - The internal, service-to-service
apply-settings-patchendpoint (see Chapter 10)
4.2 Request flow
Angular
→ MultiTenancyApi POST/GET api/v1/onboarding/* (JWT from normal login)
→ generates recommendation, stores session, generates settings patch JSON
→ on Apply: calls BackEnd's internal bridge over HTTPS
→ BackEnd POST api/internal/onboarding/apply-settings-patch (X-Shumoul-Internal-Key)
→ IAppSettingService.GetAppSetting<T>() / SetAppSetting<T>()
→ tenant's real AppSettings, in the tenant's own resolved database
Angular must never call the internal BackEnd endpoint directly. It is unauthenticated by design (service key only, no user JWT accepted) and hidden from Swagger — see Chapter 10 — Internal Apply Bridge and Chapter 15 — Security Notes.
4.3 Why a bridge instead of one host doing everything
Shumoul.MultiTenancyApi's TenantDbContext only has DbSets for central Saas-schema data (tenants,
subscriptions, onboarding, notifications, etc.) — it has no connection to any tenant's own ERP database,
and therefore no access to IAppSettingService or the real settings POCOs (CashierAppsSettings,
ProductSettings, etc.), which live in Shumoul.Application/Shumoul.Infrastructure. Rather than duplicate
tenant-database connection logic or the settings model into the MultiTenancy host, the recommendation/patch
is generated centrally and handed to the BackEnd host — which already knows how to resolve the correct
tenant database and already owns the one official settings-write path — to actually apply it.
4.4 Data locality
| Data | Lives in |
|---|---|
| Survey definitions, questions, profiles, rules | Central Saas database (Shumoul.MultiTenancyApi) |
| Tenant onboarding sessions & apply logs | Central Saas database (Shumoul.MultiTenancyApi) |
Actual AppSettings values | Each tenant's own resolved database (Shumoul.BackEnd) |
The onboarding tables never move into a tenant's own database — they are metadata about the onboarding
process, not tenant business data. Only the resulting AppSettings rows are tenant-database-local. See
Chapter 7 — Database Tables.
