Pourquoi j'ai supprimé toutes les API REST de mes SaaS — et ce que ça change pour l'utilisateur
Un SaaS classique empile les appels HTTP jusqu'à devenir lent et incohérent. Sur mes 3 derniers produits, j'ai pris le pari inverse : zéro REST entre domaines. L'état se propage tout seul à tous les écrans. Pas de polling, pas de divergence, pas de cache à invalider. Voici ce que ça change concrètement.
En bref
Un SaaS classique empile les lectures HTTP jusqu'à devenir lent et incohérent. Sur mes 3 derniers produits, j'ai pris le pari inverse : zéro REST entre domaines, l'état se propage tout seul à chaque écran (ActualLab Fusion). Pas de polling, pas de cache à invalider à la main, pas de divergence entre écrans. Ce que ça montre : une compétence d'architecture temps réel rare — penser en graphe de dépendances plutôt qu'en endpoints — appliquée du e-commerce (SaleCast) à l'échelle d'un monde multijoueur (OneRP).
Le problème avec REST
L'approche « zéro REST » avec Fusion, je l'ai d'abord mise en place sur SaleCast — un hub e-commerce où ventes, stock et prévisions doivent rester à jour en continu sur chaque écran. Mais c'est sur OneRP, une plateforme temps réel visant des milliers de joueurs sur un même monde, qu'elle a été poussée à l'extrême — et c'est le cas que je déroule ici. Dès qu'une foule d'utilisateurs génère des événements en continu (déplacements, inventaire, économie, chat), le pattern request-response devient un goulot d'étranglement.
Le constat : l'écrasante majorité de mes appels REST étaient des lectures répétitives. Le serveur recalculait les mêmes données à chaque requête. Faire du polling à la seconde sur des dizaines de services ? Impensable.
La découverte de Fusion
ActualLab Fusion change fondamentalement la donne. L'idée tient en une image : vos données se comportent comme un Google Docs — chaque écran reste à jour tout seul, sans jamais le redemander. Au lieu de REST endpoints, vous déclarez des ComputeMethod — des méthodes dont le résultat est automatiquement mis en cache et invalidé quand les données sous-jacentes changent.
[ComputeMethod]
public virtual async Task<PlayerEconomy> GetPlayerEconomy(int playerId)
{
var player = await _db.Players.FindAsync(playerId);
var accounts = await _db.BankAccounts
.Where(a => a.PlayerId == playerId)
.ToListAsync();
return new PlayerEconomy(player, accounts);
}
Ce qui se passe en coulisses :
- Premier appel → calcul réel, résultat mis en cache
- Appels suivants → cache servi instantanément
- Quand
BankAccountschange → invalidation automatique → recalcul - Tous les clients connectés reçoivent la mise à jour via WebSocket
Zero polling. Zero REST. Push automatique.
Concrètement, côté lecture, voilà ce qui change :
| REST + polling | Fusion (ComputeMethod) | |
|---|---|---|
| Lire une donnée | GET /api/economy/42 à intervalle régulier | appel de méthode, cache-hit si rien n'a bougé |
| Détecter un changement | re-poller et comparer | invalidation poussée automatiquement |
| Recalcul serveur | à chaque requête | seulement quand une dépendance change |
| Cohérence entre écrans | « best effort » (chaque écran poll à son rythme) | tous les abonnés voient la même valeur au même instant |
| Cache | à gérer/invalider à la main (Redis, TTL…) | dérivé du graphe de dépendances, jamais périmé |
L'architecture résultante
Ce que ça donne en pratique sur OneRP :
- Des dizaines de ComputeServices couvrant économie, inventaire, véhicules, propriétés, métiers...
- 0 endpoint REST — 100% Fusion RPC sur un seul canal binaire.
- Le chemin de lecture est dominé par des cache-hits : quand rien n'a changé, on ressert sans retoucher la base.
- Les mises à jour sont poussées aux clients abonnés au lieu d'être demandées — pas de polling.
Le graphe d'invalidation est le cœur du système. Quand un joueur achète un objet :
InventoryService.GetInventory()est invalidé- Ce qui invalide
PlayerService.GetPlayerWeight() - Ce qui invalide
UIService.GetHUDData() - Le HUD du joueur se met à jour automatiquement
Multi-tenancy avec Fusion
L'autre défi était le multi-tenant. Chaque serveur GTA V est un tenant isolé. La solution : combiner les Global Query Filters d'EF Core avec le cache Fusion.
// Le TenantProvider injecte automatiquement le filtre
modelBuilder.Entity<Vehicle>()
.HasQueryFilter(v => v.ServerId == _tenantProvider.CurrentServerId);
Fusion cache les résultats par clé composite (tenantId, playerId), donc pas de fuite de données entre tenants même avec le cache.
Les pièges qu'on découvre en prod
Fusion résout le polling, mais déplace la difficulté ailleurs. Trois choses apprises à la dure :
- Le fan-out d'invalidation. Un
ComputeMethodtrop central (GetHUDData()) dépend de tout — donc se fait invalider tout le temps, et recalcule tout le temps. La parade : des méthodes fines, composées, pour que seul le sous-arbre vraiment touché se recalcule. - Les Global Query Filters EF Core + le cache. Le filtre tenant doit faire partie de la
clé de cache (
(tenantId, …)), sinon deux tenants peuvent se partager un cache-hit. La composite key plus haut n'est pas un détail : c'est la frontière de sécurité. - Le
[ComputeMethod]doit être pur. S'il lit l'heure, unRandom, ou un état non tracké, Fusion ne sait pas quand l'invalider → données figées. Les effets de bord vivent ailleurs.
La vraie courbe d'apprentissage
Ce n'est pas la syntaxe Fusion qui coûte, c'est de penser en graphe de dépendances plutôt qu'en endpoints. Une fois ce déclic fait, on n'écrit plus jamais de cache à invalider à la main.
Ce que j'en retiens
Fusion n'est pas pour tout le monde. La courbe d'apprentissage est raide, la documentation est sparse, et vous devez repenser complètement votre architecture. Mais pour un système où les données changent fréquemment et doivent être poussées aux clients en temps réel, c'est imbattable.
Cette architecture a commencé sur SaleCast (forecasting e-commerce, gros modèle de domaine), je l'ai ensuite poussée à l'échelle d'un MMORPG sur OneRP, puis reprise sur un SaaS de gestion de prompts IA. À chaque fois, la suppression de REST a simplifié le code et éliminé des classes entières de bugs.
Si la dimension anti-triche et runtime contraint vous intéresse, je détaille le cas OneRP dans OneRP — viser des milliers de joueurs au même endroit.
Le futur du web est réactif, pas request-response.
Florian Sola
Développeur Senior C#/.NET/GenAI — Lead Tech & Architecte logiciel · 9 ans d'expérience
La suite logique
Ce sujet ressemble à ce que vous devez livrer ? Parlons-en.
Je prends des sujets techniques critiques — du cadrage à la production, sans dette ni dépendance après le transfert. Le plus rapide pour voir si ça colle : 30 minutes en visio.
Réponse sous 24 h — souvent bien avant.