Skip to main content
Version: Next

4. Architecture Overview

Diagrams in this chapter use Mermaid syntax inside fenced code blocks. GitHub, Docusaurus, Docsify, and most modern Markdown renderers render these natively as diagrams; if exporting to a renderer without Mermaid support, render them to SVG/PNG first and swap the code fence for an image tag referencing {{brand.cover}}-style asset paths under docs/assets/branding/.

4.1 Repository map

RepositoryRoleDistributed as
Shumoul.Saas.NotificationFrameworkContracts, Abstractions, Core, Persistence, SignalR6 versioned NuGet packages (Shumoul.Notification.*)
Shumoul.Saas.Api (this repo)ERP host: controllers, campaign engine, WhatsApp sender, adaptersApplication source
Shumoul.Saas.MultiTenancyApiLegacy SignalR hub (NotificationHub) + legacy CRUD controllersApplication source, also ships Shumoul.Framework.MultiTenancy.* packages
Shumoul.Saas.WhatsAppIntegrationWhatsApp Cloud API client (IWhatsAppService)NuGet package(s)
Shumoul.Saas.BackgroundJobsFrameworkGeneric recurring-job runtime that now wraps every notification Hangfire jobNuGet package(s)

4.2 Layer diagram

4.3 Runtime component diagram

4.4 Ownership boundaries

OwnerOwnsMust NOT own
Shumoul.Notification.ContractsEnums, cross-cutting DTOs, settings POCOs, permission string constantsAny ERP entity type, any EF Core reference
Shumoul.Notification.AbstractionsRepository/adapter interfaces (ports) the ERP host implementsConcrete implementations, IRepositoryAsync reference
Shumoul.Notification.CoreDispatch engine, retry strategies, provider resolution, health monitoringERP-specific business rules (e.g. WhatsApp business templates, campaign scheduling)
Shumoul.Notification.PersistenceEntity classes + EF configuration for framework-owned tablesAnything requiring IRepositoryAsync/ERP ApplicationDbContext directly
Shumoul.Notification.SignalRUser-ID resolution, canonical group/method-name constantsThe hub class itself (that stays host-owned, see §4.4.1)
Shumoul.Saas.ApiCampaign engine, WhatsApp business-template sender, controllers, adapters bridging IRepositoryAsync to framework portsGeneric/reusable notification logic that has no ERP dependency
Shumoul.Saas.MultiTenancyApiLegacy NotificationHub + legacy CRUD controllers ("Stack B")New feature work — this stack is frozen, not actively extended

4.4.1 Why the SignalR hub stays in MultiTenancyApi

NotificationHub (the live, in-use SignalR hub) is entity-coupled to INotificationTopicSubscriberService and INotificationConnectionService, both of which are MultiTenancyApi-specific. Per the legacy-assets verification carried out for this framework, moving it would introduce a circular dependency between MultiTenancyApi and the Notification Framework packages. It therefore stays where it is and only consumes the framework's Shumoul.Notification.SignalR package for the user-ID provider and shared constants — see Chapter 10 — SignalR.

4.5 Dependency graph

Note: Persistence and Core do not depend on each other directly — the ERP host wires them together via adapter classes (e.g. DeliveryPolicyRepositoryAdapter) that implement an Abstractions interface on top of Persistence entities queried through IRepositoryAsync. This keeps Core's dispatch logic free of any direct EF Core / IRepositoryAsync dependency.

4.6 Package graph

See Chapter 5 — Framework Packages for the full package-by-package breakdown of public APIs and responsibilities.

4.7 End-to-end flow diagrams

See Chapter 6 — Runtime Flow for the full trigger → pipeline → template → dispatch → channel → history → retry → dead letter → SignalR → in-app sequence diagram.