Events & messaging
Published language
Section intitulée « Published language »Les events inter-bounded-contexts sont les seuls éléments partagés entre modules. Ils transitent exclusivement via NATS JetStream et sont décrits sous forme de schemas Protobuf dans le package @pms/events-proto.
Enveloppe
Section intitulée « Enveloppe »Chaque event est encapsulé dans une Envelope Protobuf versionnée :
Envelope { eventId : UUID v4 (idempotence consumers) eventType : string (ex: "reservation.confirmed") occurredAt : timestamp tenantId : UUID payload : bytes (Protobuf sérialisé) signature : bytes (HMAC-SHA256, clé rotative)}La signature HMAC est reproductible (construction déterministe du message à signer) et permet de détecter toute altération en transit. Les PII ne sont jamais dans les events — seules des références opaques (IDs).
24 schemas Protobuf
Section intitulée « 24 schemas Protobuf »Le package @pms/events-proto contient 24 schemas, dont notamment :
reservation.*— created, confirmed, cancelled, no_show, checked_in, checked_out, unit_pre_assigned, unit_assignment_releasedstay.*— checked_in, checked_out, room_movedfolio.*— charge_posted, payment_posted, closedpayment.*— captured, refundeddeposit.*— recognized (ACOMPTE/ARRHES)housekeeping.*— task_completedwork_order.*— closednight_audit.*— completedidentity.*— session_revokedaccounts_receivable.*— overduetraveler_registry.*— stay_checkout_received
Outbox pattern
Section intitulée « Outbox pattern »L’atomicité entre l’état de l’agrégat, les events publiés et le journal de versions est garantie par l’outbox pattern :
- Le use-case sauvegarde l’agrégat + insère les events dans
pms.outboxdans la même transaction - Le worker
OutboxRelay(processus séparé) lit la table outbox et publie vers NATS JetStream - Après ACK NATS, la ligne outbox est marquée
published_at - En cas de panne relay, les events restent dans la table et sont réémis au redémarrage (at-least-once delivery)
[Use-case TX] → pms.outbox INSERT ↓ [OutboxRelay] → NATS JetStream ↓ [Consumers] (idempotents par eventId)17 Consumers NATS durables
Section intitulée « 17 Consumers NATS durables »Tous enregistrés dans apps/api/src/composition/consumer-worker.ts. Chaque consumer est idempotent par eventId ou hold unique.
| Consumer | Sujet | Bounded context cible |
|---|---|---|
| audit | pms.*.> | audit |
| révocation | identity.session_revoked | identity |
| payment→folio | payment.captured | folio |
| deposit-recognition ACOMPTE | deposit.recognized | fiscal-compliance |
| deposit-recognition ARRHES | deposit.recognized | fiscal-compliance |
| stay→HK | stay.checked_in | housekeeping |
| HK→inventory | housekeeping.task_completed | inventory |
| WO→inventory | work_order.closed | inventory |
| cashless-checkout | stay.checked_out | cashless |
| debtor→dunning | accounts_receivable.overdue | accounts-receivable |
| notification email | notification.requested | notification |
| notification SMS | notification.sms_requested | notification |
| distribution | reservation.confirmed | distribution |
| pos-bridge folio-closed | folio.closed | pos-bridge |
| owner-revenue folio-closed | folio.closed | owner-revenue |
| reporting (×3) | night_audit.completed | reporting |
| webhook-dispatcher | webhook.outbound_requested | public-api |
| traveler | stay.checked_out | traveler-registry |
Sujets NATS
Section intitulée « Sujets NATS »Convention de nommage : <bounded-context>.<event_type> en minuscules, séparateurs points. JetStream Stream PMS_EVENTS capture pms.>.
Idempotence
Section intitulée « Idempotence »Les consumers garantissent l’idempotence via :
- Déduplication par
eventId(tablepms.processed_events) - Advisory locks transaction-level pour les opérations destructives (pas de session locks — PgBouncer
pool_mode=transaction) Idempotency-KeyHTTP pour les opérations client (interceptor + store Redis)
Webhooks sortants (API publique)
Section intitulée « Webhooks sortants (API publique) »Les webhooks vers les intégrateurs tiers transitent par le consumer webhook-dispatcher :
- Signature HMAC sur le payload (secret par endpoint, rotation possible)
- Anti-SSRF : whitelist d’URLs cibles
- Retry automatique avec backoff
- DLQ (Dead Letter Queue) après N échecs
delivery_logappend-only (NF203 : inaltérable)