Skip to main content
Version: Latest

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 AppSettings persistence, 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-patch endpoint (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

DataLives in
Survey definitions, questions, profiles, rulesCentral Saas database (Shumoul.MultiTenancyApi)
Tenant onboarding sessions & apply logsCentral Saas database (Shumoul.MultiTenancyApi)
Actual AppSettings valuesEach 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.