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
| Repository | Role | Distributed as |
|---|---|---|
Shumoul.Saas.NotificationFramework | Contracts, Abstractions, Core, Persistence, SignalR | 6 versioned NuGet packages (Shumoul.Notification.*) |
Shumoul.Saas.Api (this repo) | ERP host: controllers, campaign engine, WhatsApp sender, adapters | Application source |
Shumoul.Saas.MultiTenancyApi | Legacy SignalR hub (NotificationHub) + legacy CRUD controllers | Application source, also ships Shumoul.Framework.MultiTenancy.* packages |
Shumoul.Saas.WhatsAppIntegration | WhatsApp Cloud API client (IWhatsAppService) | NuGet package(s) |
Shumoul.Saas.BackgroundJobsFramework | Generic recurring-job runtime that now wraps every notification Hangfire job | NuGet package(s) |
4.2 Layer diagram
4.3 Runtime component diagram
4.4 Ownership boundaries
| Owner | Owns | Must NOT own |
|---|---|---|
Shumoul.Notification.Contracts | Enums, cross-cutting DTOs, settings POCOs, permission string constants | Any ERP entity type, any EF Core reference |
Shumoul.Notification.Abstractions | Repository/adapter interfaces (ports) the ERP host implements | Concrete implementations, IRepositoryAsync reference |
Shumoul.Notification.Core | Dispatch engine, retry strategies, provider resolution, health monitoring | ERP-specific business rules (e.g. WhatsApp business templates, campaign scheduling) |
Shumoul.Notification.Persistence | Entity classes + EF configuration for framework-owned tables | Anything requiring IRepositoryAsync/ERP ApplicationDbContext directly |
Shumoul.Notification.SignalR | User-ID resolution, canonical group/method-name constants | The hub class itself (that stays host-owned, see §4.4.1) |
Shumoul.Saas.Api | Campaign engine, WhatsApp business-template sender, controllers, adapters bridging IRepositoryAsync to framework ports | Generic/reusable notification logic that has no ERP dependency |
Shumoul.Saas.MultiTenancyApi | Legacy 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.