@volontariapp/outbox 0.9.40 → 0.9.43-snap-d0fb360

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +125 -0
  3. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.43
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @volontariapp/messaging@2.10.3
9
+ - @volontariapp/database@3.4.6
10
+
11
+ ## 0.9.42
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies []:
16
+ - @volontariapp/messaging@2.10.2
17
+ - @volontariapp/testing@1.0.3
18
+ - @volontariapp/logger@0.2.7
19
+ - @volontariapp/database@3.4.5
20
+ - @volontariapp/config@3.2.2
21
+ - @volontariapp/errors@0.6.2
22
+
23
+ ## 0.9.41
24
+
25
+ ### Patch Changes
26
+
27
+ - README bump
28
+
29
+ - Updated dependencies []:
30
+ - @volontariapp/config@3.2.1
31
+ - @volontariapp/database@3.4.4
32
+ - @volontariapp/errors@0.6.1
33
+ - @volontariapp/logger@0.2.6
34
+ - @volontariapp/messaging@2.10.1
35
+ - @volontariapp/shared@0.8.1
36
+ - @volontariapp/testing@1.0.2
37
+
3
38
  ## 0.9.40
4
39
 
5
40
  ### Patch Changes
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # @volontariapp/outbox
2
+
3
+ ## Overview & The Transactional Outbox Pattern
4
+
5
+ Le package `outbox` est le pilier central de l'architecture asynchrone et événementielle de Volontariapp.
6
+ Il résout le **Problème de la Double Écriture (Dual Write Problem)** : comment garantir qu'une entité métier est sauvegardée en base ET qu'un message/job est bien envoyé à Redis/BullMQ sans risque d'incohérence si l'un des deux systèmes tombe ?
7
+
8
+ La solution implémentée ici est agnostique de NestJS (Node.js pur) pour des performances optimales et une séparation claire des responsabilités.
9
+
10
+ ## Architecture et Rôles des Composants
11
+
12
+ L'architecture interne de l'Outbox est divisée en plusieurs responsabilités claires, du polling de la base de données jusqu'à l'envoi physique dans le Broker (Redis).
13
+
14
+ ```mermaid
15
+ classDiagram
16
+ direction TB
17
+
18
+ class Writer {
19
+ <<Interface Microservice>>
20
+ +write(payload, entityManager)
21
+ }
22
+
23
+ class Consumer {
24
+ <<Polling Daemon>>
25
+ +pollPendingRows()
26
+ }
27
+
28
+ class Dispatcher {
29
+ <<Router & Orchestrator>>
30
+ +dispatch(rows)
31
+ }
32
+
33
+ class Pusher {
34
+ <<Infrastructure Adapter>>
35
+ +pushToBroker(message)
36
+ }
37
+
38
+ class DB {
39
+ <<PostgreSQL>>
40
+ jobs_outbox
41
+ event_outbox
42
+ }
43
+
44
+ class Redis {
45
+ <<Message Broker>>
46
+ BullMQ
47
+ Redis Streams
48
+ }
49
+
50
+ Writer --> DB : Insère dans la même transaction que le métier
51
+ Consumer --> DB : SELECT WHERE status = 'Pending'
52
+ Consumer --> Dispatcher : Envoie les lignes récupérées
53
+ Dispatcher --> Pusher : Aiguille selon le type (Job vs Event)
54
+ Pusher --> Redis : Publie physiquement
55
+ Dispatcher --> DB : UPDATE status = 'Done'
56
+ ```
57
+
58
+ ### Détail des Composants
59
+ - **Writers** : Utilisés par les microservices (ex: `ms-event`) pour insérer une intention d'action asynchrone (Job ou Domain Event) dans la même transaction SQL que l'action métier.
60
+ - **Consumers** : Boucles infinies tournant en tâche de fond (ou dans un Cron Kubernetes) qui lisent les tables d'outbox à la recherche de lignes `Pending`.
61
+ - **Dispatchers** : Reçoivent les lignes du Consumer. Ils orchestrent la logique de retry et décident à quel Pusher déléguer (ex: un Job va dans BullMQ, un Domain Event va dans un Redis Stream). Une fois poussé avec succès, le Dispatcher marque la ligne comme `Done` en SQL.
62
+ - **Pushers** : Adaptateurs de bas niveau responsables de la connexion réseau avec Redis/Kafka pour l'envoi du binaire/JSON.
63
+
64
+ ## Flux d'Exécution (Séquence)
65
+
66
+ ```mermaid
67
+ sequenceDiagram
68
+ participant API as Microservice (ex: ms-event)
69
+ participant DB as PostgreSQL (Transaction)
70
+ participant Consumer as Outbox Consumer
71
+ participant Dispatcher as Outbox Dispatcher
72
+ participant Pusher as Redis Pusher
73
+
74
+ API->>DB: BEGIN Transaction
75
+ API->>DB: INSERT INTO event (logique métier)
76
+ API->>DB: INSERT INTO jobs_outbox (status = 'Pending')
77
+ API->>DB: COMMIT Transaction
78
+
79
+ loop Polling (Background)
80
+ Consumer->>DB: SELECT * FROM jobs_outbox WHERE status = 'Pending' LIMIT 10
81
+ Consumer->>Dispatcher: dispatch(rows)
82
+ Dispatcher->>DB: UPDATE jobs_outbox SET status = 'Processing'
83
+ Dispatcher->>Pusher: pushToBroker(payload)
84
+ Pusher-->>Dispatcher: Ack (Poussé avec succès)
85
+ Dispatcher->>DB: UPDATE jobs_outbox SET status = 'Done'
86
+ end
87
+ ```
88
+
89
+ ## Structure des Dossiers
90
+
91
+ ```text
92
+ src/
93
+ ├── writers/ # Classes utilisées par les MS pour écrire dans la DB Outbox
94
+ ├── consumers/ # Boucles de polling récupérant les lignes de DB 'Pending'
95
+ ├── dispatchers/ # Logique de routage vers BullMQ ou Redis Stream
96
+ ├── pushers/ # Classes poussant physiquement vers Redis
97
+ └── repositories/ # Accès direct aux tables `jobs_outbox` / `event_outbox`
98
+ ```
99
+
100
+ ## Exemple d'Implémentation
101
+
102
+ ### Écriture Transactionnelle depuis un Microservice
103
+
104
+ Le microservice utilise le `Writer` fourni par la librairie en lui passant le gestionnaire de transaction TypeORM (EntityManager).
105
+
106
+ ```typescript
107
+ import { JobsOutboxWriter } from '@volontariapp/outbox';
108
+
109
+ export class EventService {
110
+ async createEventWithJob(payload: any, manager: EntityManager) {
111
+ // 1. Sauvegarde métier classique
112
+ const event = await manager.save(EventEntity, payload);
113
+
114
+ // 2. Écriture du Job de façon transactionnelle
115
+ const writer = new JobsOutboxWriter(manager);
116
+ await writer.write({
117
+ queueName: 'event-processing',
118
+ jobName: 'notify-followers',
119
+ payload: { eventId: event.id },
120
+ options: { delay: 5000 }
121
+ });
122
+ // Si la transaction échoue, le job n'est pas inséré : Cohérence absolue.
123
+ }
124
+ }
125
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volontariapp/outbox",
3
- "version": "0.9.40",
3
+ "version": "0.9.43-snap-d0fb360",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -41,13 +41,13 @@
41
41
  "db:down": "docker compose -f ../../ci-tools/testing/docker-compose.yml --profile test stop redis"
42
42
  },
43
43
  "dependencies": {
44
- "@volontariapp/config": "3.2.0",
45
- "@volontariapp/database": "3.4.3",
46
- "@volontariapp/errors": "0.6.0",
47
- "@volontariapp/logger": "0.2.5",
48
- "@volontariapp/messaging": "2.10.0",
49
- "@volontariapp/shared": "0.8.0",
50
- "@volontariapp/testing": "1.0.1",
44
+ "@volontariapp/config": "3.2.2",
45
+ "@volontariapp/database": "3.4.6-snap-d0fb360",
46
+ "@volontariapp/errors": "0.6.2",
47
+ "@volontariapp/logger": "0.2.7",
48
+ "@volontariapp/messaging": "2.10.3-snap-d0fb360",
49
+ "@volontariapp/shared": "0.8.1",
50
+ "@volontariapp/testing": "1.0.3",
51
51
  "bullmq": "^5.76.5",
52
52
  "ioredis": "^5.10.1"
53
53
  },
@@ -56,7 +56,7 @@
56
56
  "@types/jest": "^30.0.0",
57
57
  "@types/node": "^22.10.7",
58
58
  "@types/pg": "^8",
59
- "@volontariapp/testing": "1.0.1",
59
+ "@volontariapp/testing": "1.0.3",
60
60
  "jest": "^30.3.0",
61
61
  "ts-jest": "^29.4.6",
62
62
  "ts-node": "^10.9.2",