Aller au contenu
4 min de lecture Moustakime KIFIA

Deux apps Next.js sur deux domaines, une seule API

Pourquoi on a séparé portail et console opérateur en deux apps Next.js, et ce que cette séparation a retiré de la base.

  • Architecture
  • Multi-tenant
  • Auth

Le sujet n’est pas de raconter un split de repo pour lui-même. Le sujet, c’est de savoir quand deux expériences produit doivent devenir deux frontaux distincts pour garder une base lisible.

Quand deux utilisateurs ne vivent plus le même produit, forcer une seule application web à porter les deux finit souvent par diffuser la complexité partout. C’est exactement le moment où il faut se demander si la mutualisation apparente ne coûte pas plus cher que deux frontaux bien posés.

Le problème à résoudre

Le point de départ était une seule application Next.js capable de regarder le host courant pour se comporter tantôt comme un portail client, tantôt comme une console opérateur.

Sur le papier, l’idée paraît économique :

  • une seule app
  • un seul pipeline
  • un seul layout global
  • quelques conditions sur le host

Dans les faits, cette logique fuit vite partout :

  • middleware
  • navigation
  • layouts
  • gestion de session
  • appels API
  • tests

La question n’était donc pas “comment gérer deux domaines dans Next.js ?”. La vraie question était : où veut-on que la complexité vive.

La solution retenue

L’architecture finale est simple à décrire :

  • apps/portal pour cercly.co
  • apps/operator pour operator.cercly.co
  • une seule operator-api
  • deux cookies distincts : tenant_session et operator_session

Le vrai sujet n’est pas le multi-domaines en soi. Le vrai sujet, c’est l’endroit où la complexité vit.

Avec une seule app, la complexité se diffuse partout. Avec deux apps, elle remonte à la frontière entre les clients et l’API.

Pourquoi cette solution tient bien dans ce type de plateforme

Cette solution tient bien parce qu’elle :

  • isole naturellement tenant_session et operator_session
  • sépare le portail utilisateur du cockpit interne
  • permet des déploiements indépendants sans arbitrage permanent
  • simplifie les middlewares et la lecture des permissions
  • rend les layouts et les routes plus prévisibles

Dit autrement : on n’a pas dupliqué un produit, on a remis chaque expérience dans le bon périmètre.

Comment on l’implémente

Le bon niveau de code à montrer ici n’est pas un gros diff de repo. Ce sont plutôt deux points d’appui très concrets :

  • le middleware portal qui lit le cookie et injecte x-cercly-tenant-access sur les routes /api/*
  • les labels Traefik qui routent les deux domaines vers les bons frontaux

Le but n’est pas de montrer beaucoup de code. Le but est de rendre visible le découpage et de montrer où la complexité a été déplacée.

Un exemple de logique qui reste simple une fois le split fait :

if (!tenantSessionCookie) {
  return NextResponse.redirect(new URL("/auth/login", request.url));
}

Le point important, ce n’est pas la ligne ci-dessus. C’est le fait qu’elle n’a plus besoin de se demander si elle tourne dans le portail, l’opérateur, ou une troisième variante host-aware.

Ce que cette séparation a retiré de la base

Le gain le plus sous-estimé est souvent négatif : ce qu’on retire.

On a retiré :

  • des branches conditionnelles liées au host
  • des layouts qui changeaient de rôle selon le domaine
  • des cookies qui n’avaient pas la même sémantique selon le contexte
  • des tests qui devaient simuler deux produits dans une seule application

Une architecture saine se voit souvent dans la quantité de logique qu’on n’a plus besoin d’écrire.

Ce que j’ai appris en l’implémentant

Le piège est simple : on pense économiser du temps avec une seule app. En pratique, on achète une dette qui fuit dans toute la base.

La phrase importante à garder est celle-ci : séparer en deux apps a coûté un week-end, pas un mois.

Je préfère payer une petite séparation une fois, plutôt que de maintenir pendant des mois un faux monolithe frontend qui n’a plus de vraie cohérence produit.

Quand ne pas faire ce split

Il ne faut pas transformer ce retour d’expérience en règle absolue.

Si les deux expériences partagent réellement :

  • la même navigation
  • les mêmes permissions
  • le même rythme de release
  • la même logique de session

alors une seule application peut rester le meilleur choix.

Ici, ce n’était plus vrai. Et une fois ce constat posé, la séparation était la solution la plus lisible.

Continuer la lecture

Quelques articles relies pour renforcer le maillage interne et prolonger les sujets techniques voisins.

2 min

Construire un SaaS full stack en lead tech

Retour sur les choix techniques de Cercly — de l'environnement de dev à la prod, en passant par l'archi micro-services, le mobile et l'accélération par l'IA.

  • Architecture
  • Engineering
  • DevOps
  • SaaS
Lire la note