veza/docs/adr/ADR-002-chat-server.md
senke 431ad133e2 docs(v0.502): plan d'implémentation Chat Server Rewrite (Rust → Go)
- Create ADR-002-chat-server.md: decision to rewrite Rust chat in Go
- Rewrite V0_502_RELEASE_SCOPE.md with 4 detailed lots (34 tasks)
- Create PLAN_V0_502_IMPLEMENTATION.md with 6 sprints and commit instructions
- Update .cursorrules scope reference for v0.502
2026-02-22 20:26:18 +01:00

6 KiB

ADR-002 : Réécriture du Chat Server Rust → Go

Date : 2026-02-22 Statut : Accepté Décideurs : Équipe technique Veza Réf. scope : V0_502_RELEASE_SCOPE.md


Contexte

Le chat server actuel (veza-chat-server) est implémenté en Rust avec :

  • Axum 0.8 pour le serveur HTTP et WebSocket
  • SQLx 0.8 pour la persistence PostgreSQL
  • Tokio-tungstenite pour les connexions WebSocket
  • Lapin 2.3 pour RabbitMQ (non utilisé en production)
  • Tonic 0.11 / Prost 0.12 pour gRPC (stub, jamais intégré)

Le serveur compile mais n'est pas intégré en production :

  • Boot mode OFF dans Docker Compose
  • Le frontend utilise des mocks MSW pour le chat
  • L'authentification WebSocket est cassée (TokenStorage.getAccessToken() retourne null)
  • gRPC et RabbitMQ ne sont pas utilisés

Par ailleurs, le backend Go possède déjà :

  • Des modèles GORM pour Room, RoomMember, Message
  • Un RoomRepository et un ChatRepository (raw SQL)
  • Un RoomService avec CRUD complet
  • Un RoomHandler avec routes REST /conversations/*
  • Un ChatService avec génération de token JWT
  • Un pattern WebSocket fonctionnel (playback_websocket_handler.go avec coder/websocket)

Décision

Réécrire le chat server en Go, intégré directement dans le backend API existant (veza-backend-api).

Approche retenue

Ajouter un handler WebSocket Go au backend existant plutôt que de maintenir un service Rust séparé.


Justification

1. Complexité injustifiée

Le chat server Rust introduit un second langage, un second runtime, des dépendances séparées (SQLx vs GORM, Axum vs Gin) pour une fonctionnalité qui ne requiert pas les performances extrêmes de Rust. Le chat textuel est I/O-bound, pas CPU-bound.

2. Duplication de l'infrastructure

  • Deux systèmes d'authentification JWT (Go + Rust) à maintenir en synchronisation
  • Deux connexions PostgreSQL séparées pour les mêmes tables
  • Deux systèmes de logging, monitoring et configuration

3. Pattern existant en Go

Le playback_websocket_handler.go démontre que Go avec coder/websocket gère efficacement les connexions WebSocket. Le pattern Client/Hub avec goroutines readPump/writePump est éprouvé.

4. Modèles et repositories existants

Les modèles Room, RoomMember, Message et les repositories GORM sont déjà implémentés côté Go. Le chat server Go peut les réutiliser directement.

5. Simplification opérationnelle

Un seul binaire à déployer, monitorer et scaler. Pas de coordination inter-services pour le chat.


Conséquences

Positives

  • Un seul langage pour le backend (Go) — maintenance simplifiée
  • Réutilisation des modèles, repositories, middleware auth existants
  • Authentification unifiée — même JWT, même middleware
  • Déploiement simplifié — un seul conteneur pour API + WebSocket
  • Monitoring unifié — un seul jeu de métriques Prometheus

Négatives

  • Effort de développement : réécriture complète du handler WebSocket (~1200 LOC Rust → Go)
  • Migration frontend : changement d'URL WebSocket et potentiels ajustements de protocole
  • Pas de séparation des responsabilités : le backend Go grossit

Risques mitigés

Risque Mitigation
Performance WebSocket Go Benchmark : Go gère 10k+ connexions WS sans problème pour du chat textuel
Régression fonctionnelle Feature parity checklist + tests E2E avant la migration
Downtime pendant migration Frontend pointe vers la nouvelle URL via variable d'environnement

Spécifications techniques

Protocole WebSocket

Le protocole JSON existant est conservé tel quel pour assurer la compatibilité frontend :

Messages entrants (client → serveur) :

  • SendMessage, EditMessage, DeleteMessage
  • JoinConversation, LeaveConversation
  • MarkAsRead, Delivered, Typing
  • AddReaction, RemoveReaction
  • FetchHistory, SearchMessages, SyncMessages
  • CallOffer, CallAnswer, ICECandidate, CallHangup, CallReject
  • Ping

Messages sortants (serveur → client) :

  • NewMessage, MessageEdited, MessageDeleted
  • UserTyping, MessageRead, MessageDelivered
  • ReactionAdded, ReactionRemoved
  • HistoryChunk, SearchResults, SyncChunk
  • CallOffer, CallAnswer, ICECandidate, CallHangup, CallRejected
  • ActionConfirmed, Error, Pong

Endpoint WebSocket

GET /api/v1/ws?token={jwt_token}

Authentification

  • Token JWT généré via POST /api/v1/chat/token (existant)
  • Validé par le même middleware JWT que le backend Go
  • Claims : sub (user_id), name (username), aud ("veza-chat"), exp

Persistence

  • GORM avec les modèles existants (Room, RoomMember, Message)
  • Nouvelles tables : read_receipts, delivered_status, message_reactions

Broadcasting multi-instance

  • Redis PubSub pour synchroniser les messages entre instances Go
  • Canal par room : chat:room:{room_id}
  • Canal global : chat:presence

Alternatives considérées

A. Corriger le chat server Rust existant

Rejeté : nécessiterait de résoudre l'intégration gRPC, la synchronisation JWT, et la maintenance d'un second langage. Effort comparable à la réécriture en Go avec un résultat moins maintenable.

B. Utiliser un service chat tiers (Stream, Ably)

Rejeté : coût récurrent, dépendance externe, données hors contrôle. Non aligné avec la philosophie self-hosted de Veza.

C. Garder Rust mais simplifier (supprimer gRPC, RabbitMQ)

Rejeté : reste un second langage à maintenir, duplication JWT et DB connections.


Références

  • ADR-001-rust-services.md — Architecture initiale Go + Rust + React
  • V0_502_RELEASE_SCOPE.md — Scope de la réécriture
  • veza-chat-server/src/websocket/handler.rs — Handler WebSocket Rust à remplacer
  • veza-backend-api/internal/handlers/playback_websocket_handler.go — Pattern WebSocket Go de référence