2. Architecture
2.1 Two-Layer Model
2.2 Standalone Library Boundary
The library exposes exactly three public interfaces (Abstractions package) and never reaches upward into
ERP concepts. It has no DbContext, no TenantId, no permission checks, and no knowledge of "conversations"
or "customers" — only "messages," "templates," and "webhooks" in Meta's own vocabulary.
2.3 ERP Host Boundary
Shumoul.Saas.Api is the only consumer of this library. It owns all persistence (WhatsAppConversation,
WhatsAppMessage, WhatsAppConversationNote, WhatsAppConversationTag, WhatsAppSavedReply, etc.), all
tenant resolution, all permission checks ([MustHavePermission]), and the entire controller/DTO surface
documented in §6 API Reference.
2.4 Why There Is Exactly One Meta Client
Prior to the Phase 1 Runtime Consolidation, two independent Graph API HTTP clients existed:
WhatsAppCloudApiClient (this library) for most flows, and a hand-rolled HttpClient inside the ERP's
TenantWhatsAppSender hardcoded to Graph API v19.0. As of Phase 1, TenantWhatsAppSender constructs a
WhatsAppCloudApiClient instance per call — merging the tenant's own credentials
(WhatsAppOperationalSettings.AccessToken/PhoneNumberId/BusinessAccountId) with the platform's central
BaseUrl/GraphApiVersion — instead of re-implementing the HTTP call. This required zero changes to the
library's public API, because WhatsAppCloudApiClient's constructor and WhatsAppCloudApiOptions were
already fully public. There is now exactly one Meta Graph API client class in the entire solution.
The one remaining direct-HTTP call outside this client is TenantWhatsAppSender.TestConnectionAsync, a
business-account lookup (GET {BaseUrl}/{GraphApiVersion}/{BusinessAccountId}?fields=id,name) with no
equivalent method on IWhatsAppService — not a duplicate, just a narrow diagnostic call that now also reads
BaseUrl/GraphApiVersion from the same central configuration.
2.5 Dependency Graph
2.6 Package Graph
2.7 Runtime Ownership Map
| Concern | Owner |
|---|---|
| Meta Cloud API HTTP client | Library (WhatsAppCloudApiClient) — the only one in the solution |
| Signature validation | Library (WhatsAppSignatureValidator) |
| Webhook parsing | Library (WhatsAppWebhookParser) |
| Provider DTOs / Meta-specific models | Library (Contracts) |
| Inbox / Conversations / Notes / Tags / Saved Replies / Timeline / Presence | ERP |
SignalR real-time layer (WhatsAppHub) | ERP |
| Communication module (tenant document sends) | ERP |
| OTP / Activation business messages | ERP |
| Phone number normalization | ERP (WhatsAppPhoneHelper) — deliberately never moved into the library |
| Controllers, permissions, routes, database schema | ERP |
2.8 Compliance With Platform Architecture Standards
- Dependency Law: the ERP consumes the library only via
PackageReference, neverProjectReference. - Adapter Standard: the Notification Framework's bridge from its generic channel contract to
IWhatsAppService(WhatsAppChannelProviderAdapter→MetaWhatsAppProvider) is a Host Adapter, not a library concern. - ADR-001: the boundary decision recorded in the library's own repository — see §1.4.
