190 lines
8.9 KiB
Markdown
190 lines
8.9 KiB
Markdown
# Système de notification de l'application
|
||
## Vue d'ensemble
|
||
Le système de notification de l'application permet d'informer les utilisateurs de diverse action.
|
||
|
||
## Architecture
|
||
### Composants principaux
|
||
1. **Service de Notification** : Gère la création, l'envoi et le suivi des notifications.
|
||
2. **Interface Utilisateur** : Affiche les notifications aux utilisateurs via des pops-ups, des bannières ou des emails.
|
||
3. **Template Email** : Modèles prédéfinis pour les notifications par email.
|
||
4. **Type D'action** : Enum d'action qui déclenche des notifications (ex: nouvel utilisateur, utilisateur actif, ...).
|
||
|
||
### Service de Notification
|
||
Le service de notification est responsable de la gestion des notifications. Il inclut les fonctionnalités suivantes :
|
||
- Création de notifications basées sur des événements spécifiques.
|
||
- Envoi de notifications via différents canaux (email, in-app).
|
||
- Suivi de l'état des notifications (envoyé, lu, etc.).
|
||
|
||
### Interface Utilisateur
|
||
L'interface utilisateur affiche les notifications de manière conviviale. Les notifications peuvent apparaître sous forme
|
||
de pops-ups, de bannières ou d'emails. (Possibilité d'intéragir avec les notifications)
|
||
|
||
### Template Email
|
||
Les templates email sont utilisés pour formater les notifications envoyées par email. Chaque type de notification
|
||
a son propre template pour assurer une communication claire et cohérente.
|
||
|
||
|
||
### Type d'action
|
||
```
|
||
enum ActionType: String {
|
||
case NewUser = "NEW_USER";
|
||
case ActiveUser = "ACTIVE_USER";
|
||
case PasswordReset = "PASSWORD_RESET";
|
||
case SubscriptionExpired = "SUBSCRIPTION_EXPIRED";
|
||
case OrganizationInvited = "ORGANIZATION_INVITED";
|
||
case OrganizationInactive = "ORGANIZATION_INACTIVE";
|
||
case OrganizationDeleted = "ORGANIZATION_DELETED";
|
||
case OrginizationUserInvited = "ORGANIZATION_USER_INVITED";
|
||
case UserDeleted = "USER_DELETED";
|
||
}
|
||
```
|
||
|
||
## Flux de travail
|
||
1. L’administrateur crée un utilisateur depuis l’interface (formulaire “Créer un utilisateur”).
|
||
2. Le contrôleur valide la requête et appelle le cas d’usage UserAdministrationService->handle(ActionType::NewUser, $admin, $payload).
|
||
3. Le service crée l’utilisateur en base avec le statut INVITED, associe l’organisation de l’admin, et génère un lien signé/jeton de setup de mot de passe (TTL).
|
||
4. Le service publie un événement de domaine UserInvitedEvent { userId, adminId, organizationId } sur Messenger (transport async).
|
||
5. Handler async A — SendUserInvitationEmailHandler:
|
||
6. Construit l’email via Symfony Mailer + Twig (emails/user_invitation.html.twig) avec le lien de définition de mot de passe.
|
||
7. Envoie le mail à l’utilisateur invité.
|
||
8. Handler async B — NotifyAdminInvitationSentHandler:
|
||
9. Crée une notification interne (Notifier, canal “in‑app”).
|
||
10. Pousse un événement temps réel via Mercure sur le topic admin/{adminId}/events avec le type INVITATION_EMAIL_SENT.
|
||
11. L’UI admin affiche un toast/bannière confirmant “Email d’invitation envoyé”.
|
||
12. L’utilisateur ouvre l’email et clique le lien de définition de mot de passe.
|
||
13. Le PasswordSetupController vérifie la signature/le jeton et la validité (TTL), affiche le formulaire, puis enregistre le nouveau mot de passe.
|
||
14. À la réussite, l’utilisateur passe au statut ACTIVE et l’action publie UserActivatedEvent { userId, adminId, organizationId } sur Messenger (async).
|
||
15. Handler async C — NotifyAdminUserActivatedHandler:
|
||
16. Crée une notification interne (Notifier, canal “in‑app”) “Compte activé”.
|
||
17. Pousse un événement Mercure sur admin/{adminId}/events avec le type USER_ACTIVATED.
|
||
18. L’UI admin met à jour la liste des membres (badge “Actif”) et affiche un toast confirmant l’activation.
|
||
19. Journalisation/Audit:
|
||
20. Chaque handler écrit une trace (succès/échec) en base ou dans un EmailLog/NotificationLog.
|
||
21. En cas d’échec d’envoi, Messenger applique la stratégie de retry puis bascule en file failed si nécessaire (tableau de bord de supervision).
|
||
22. Cas “utilisateur existant ajouté à une autre organisation”:
|
||
23. Si l’email existe déjà, on rattache l’utilisateur à la nouvelle organisation et on publie OrganizationUserInvitedEvent.
|
||
24. Handler dédié envoie un email d’information (“Vous avez été ajouté à une nouvelle organisation”) et notifie l’admin via Notifier + Mercure.
|
||
25. Cas d’actions dérivées par enum:
|
||
26. ActionType::NewUser → déclenche UserInvitedEvent (steps 3–6).
|
||
27. ActionType::ActiveUser (si activé par un flux admin) → déclenche directement UserActivatedEvent (steps 9–10).
|
||
28. ActionType::OrganizationUserInvited → flux similaire au point 12 pour la multi‑organisation.
|
||
29. Autres actions (PasswordReset, UserDeleted, etc.) suivent le même patron: contrôleur → service (match enum) → événement Messenger → handlers (Mailer/Notifier/Mercure) → UI temps réel.
|
||
|
||
## Stack technologique
|
||
- Symfony Messenger: asynchrone, retries, découplage des I/O lents.
|
||
- Symfony Mailer + Twig: emails d’invitation et d’information.
|
||
- Symfony Notifier (canal in‑app) + Mercure: notifications persistées + push temps réel vers l’UI admin.
|
||
- Enum ActionType: routage clair dans l’application, évite la logique string‑based.
|
||
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
%% Couche 1: Action initiale
|
||
A[User action event - Admin cree un utilisateur] --> B[HTTP controller API - Symfony]
|
||
B --> C[Domain service - UserAdministrationService]
|
||
C -->|Inspecte enum ActionType::NewUser| C1[Create user - status INVITED - liaison organisation - genere lien jeton mot de passe TTL]
|
||
C1 --> D[Dispatch UserInvitedEvent - userId adminId organizationId - vers Symfony Messenger bus]
|
||
|
||
%% Couche 2: Messaging / Infra
|
||
D --> E[Transport async - AMQP / Redis / Doctrine]
|
||
E --> RQ[Retry queue]
|
||
E --> FQ[Failed queue - dead letter]
|
||
E --> W[Workers Messenger]
|
||
F[Supervisor / systemd] --> W
|
||
|
||
%% Monolog transversal (logs a chaque etape)
|
||
A --> LOG_GLOBAL[Monolog - log event initial]
|
||
B --> LOG_GLOBAL
|
||
C --> LOG_GLOBAL
|
||
C1 --> LOG_GLOBAL
|
||
D --> LOG_GLOBAL
|
||
E --> LOG_GLOBAL
|
||
RQ --> LOG_GLOBAL
|
||
FQ --> LOG_GLOBAL
|
||
W --> LOG_GLOBAL
|
||
|
||
%% Handlers pour l'invitation
|
||
W --> H1[Handler A - Symfony Mailer + Twig]
|
||
H1 --> H1o[Email d'invitation avec lien setup mot de passe]
|
||
H1 --> LOG_GLOBAL
|
||
|
||
W --> H2[Handler B - Symfony Notifier in-app]
|
||
H2 --> UI1[Notification UI admin - Email d'invitation envoye]
|
||
H2 --> LOG_GLOBAL
|
||
|
||
W -. optionnel .-> WH1[Webhook HTTP sortant - invitation envoyee]
|
||
WH1 --> LOG_GLOBAL
|
||
W -. optionnel .-> SMS1[SMS gateway - SMS invitation]
|
||
SMS1 --> LOG_GLOBAL
|
||
W -. optionnel .-> PUSH1[Mobile push service - notification mobile]
|
||
PUSH1 --> LOG_GLOBAL
|
||
|
||
RQ --> METRICS[Metrics et dashboard]
|
||
FQ --> METRICS
|
||
LOG_GLOBAL --> METRICS
|
||
|
||
%% Flux activation utilisateur
|
||
subgraph Activation du compte
|
||
UA[User action event - Invite clique le lien] --> PS[HTTP controller API - PasswordSetupController]
|
||
PS -->|Verifie signature et TTL| PSOK[Set password - user status ACTIVE]
|
||
PS --> LOG_GLOBAL
|
||
PSOK --> LOG_GLOBAL
|
||
|
||
PSOK --> D2[Dispatch UserActivatedEvent - userId adminId organizationId - vers Messenger bus]
|
||
D2 --> E2[Transport async]
|
||
E2 --> RQ2[Retry queue]
|
||
E2 --> FQ2[Failed queue]
|
||
E2 --> W2[Workers Messenger]
|
||
F --> W2
|
||
|
||
D2 --> LOG_GLOBAL
|
||
E2 --> LOG_GLOBAL
|
||
RQ2 --> LOG_GLOBAL
|
||
FQ2 --> LOG_GLOBAL
|
||
W2 --> LOG_GLOBAL
|
||
|
||
W2 --> H3[Handler C - Notifier in-app]
|
||
H3 --> UI2[Notification UI admin - Compte active]
|
||
H3 --> LOG_GLOBAL
|
||
|
||
W2 -. optionnel .-> WH2[Webhook HTTP sortant - user active]
|
||
WH2 --> LOG_GLOBAL
|
||
W2 -. optionnel .-> MAIL2[Mailer ou SMS ou Push - confirmation utilisateur]
|
||
MAIL2 --> LOG_GLOBAL
|
||
|
||
RQ2 --> METRICS
|
||
FQ2 --> METRICS
|
||
end
|
||
|
||
%% Cas particulier : utilisateur existant ajoute a une nouvelle organisation
|
||
C -->|Email deja existant| SP1[Rattache a nouvelle organisation]
|
||
SP1 --> LOG_GLOBAL
|
||
SP1 --> D3[Dispatch OrganizationUserInvitedEvent]
|
||
D3 --> E3[Transport async] --> W3[Workers]
|
||
F --> W3
|
||
D3 --> LOG_GLOBAL
|
||
E3 --> LOG_GLOBAL
|
||
W3 --> LOG_GLOBAL
|
||
|
||
W3 --> M3[Mailer - ajoute a une nouvelle organisation]
|
||
M3 --> LOG_GLOBAL
|
||
W3 --> N3[Notifier in-app - toast admin Utilisateur ajoute]
|
||
N3 --> LOG_GLOBAL
|
||
W3 -. optionnel .-> WH3[Webhook ou SMS ou Mobile]
|
||
WH3 --> LOG_GLOBAL
|
||
|
||
M3 --> METRICS
|
||
N3 --> METRICS
|
||
WH3 --> METRICS
|
||
|
||
%% Styles
|
||
classDef infra fill:#e8f0fe,stroke:#5b8def,stroke-width:1px;
|
||
classDef handler fill:#dcf7e9,stroke:#2ea66a,stroke-width:1px;
|
||
classDef ui fill:#f0d9ff,stroke:#9c27b0,stroke-width:1px;
|
||
classDef audit fill:#eeeeee,stroke:#9e9e9e,stroke-width:1px;
|
||
|
||
class E,E2,E3,RQ,FQ,RQ2,FQ2,METRICS infra;
|
||
class W,W2,W3,H1,H2,H3,M3,N3 handler;
|
||
class H1o,UI1,UI2 ui;
|
||
class LOG_GLOBAL audit;
|
||
``` |