Skip to content

Frontière Auth Service vs Orchestrator Service

Question Architecturale

Est-ce pertinent de déporter les logiques multi-services du service Auth vers l'Orchestrator ?

Réponse : OUI, absolument ! Selon l'architecture documentée, l'Orchestrator est le seul service habilité à orchestrer les workflows multi-services.


Principe Fondamental

Règle d'Or (Architecture Documentée)

Les services métier (Customer, Ledger, Notification) ne sont JAMAIS appelés directement par les clients ou par d'autres services métier.

Toute opération multi-services passe par l'Orchestrator qui garantit la cohérence du workflow.

Conséquence pour Auth

Le service Auth doit se concentrer uniquement sur :

  • ✅ Authentification pure (login, token validation, refresh)
  • ✅ Gestion des sessions (logout)
  • ✅ Gestion OTP (envoi/vérification)
  • ✅ Gestion PIN (vérification, changement)

Le service Orchestrator doit gérer :

  • ✅ Workflows multi-services (Register, ResetPin avec Customer, etc.)
  • ✅ Transactions distribuées (Saga pattern)
  • ✅ Compensation en cas d'échec

Analyse des Workflows Actuels

1. Workflows Purement Auth (RESTENT dans Auth) ✅

Ces workflows n'appellent aucun autre service métier :

WorkflowEndpointServices appelésAction
LoginPOST /auth/loginAuth0 uniquement✅ Reste dans Auth
Login Phone-PINPOST /auth/login/phone-pinAuth0 + Customer (⚠️)⚠️ Voir analyse ci-dessous
Validate TokenGET /auth/meAuth0 uniquement✅ Reste dans Auth
Refresh TokenPOST /auth/refreshAuth0 uniquement✅ Reste dans Auth
LogoutPOST /auth/logoutAuth0 + Redis uniquement✅ Reste dans Auth
Send OTPPOST /auth/send-otpRedis uniquement✅ Reste dans Auth
Verify OTPPOST /auth/verify-otpRedis uniquement✅ Reste dans Auth
Check PINPOST /auth/check-pinAuth0 uniquement✅ Reste dans Auth
Change PINPOST /auth/change-pinAuth0 uniquement✅ Reste dans Auth

2. Workflows Multi-Services (DÉPLACER vers Orchestrator) ❌

Ces workflows appellent plusieurs services métier :

WorkflowEndpointServices appelésAction
RegisterPOST /auth/registerAuth0 + Local DB + Customer + LedgerDéplacer vers Orchestrator
Register MerchantPOST /auth/register/merchantAuth0 + Local DB + Customer + LedgerDéplacer vers Orchestrator
Register AgentPOST /auth/register/agentAuth0 + Local DB + Customer + LedgerDéplacer vers Orchestrator
Register AdminPOST /auth/register/adminAuth0 + Local DB + CustomerDéplacer vers Orchestrator
Reset PINPOST /auth/reset-pin/confirmOTP + Customer + Auth0Déplacer vers Orchestrator

3. Workflows Ambigus (À Analyser) ⚠️

WorkflowEndpointServices appelésAnalyse
Login Phone-PINPOST /auth/login/phone-pinAuth0 + Customer (getPersonByPhone)⚠️ Option 1 : Rester dans Auth si Customer est juste pour résoudre le user
Option 2 : Déplacer si c'est un workflow métier complexe

Détail des Workflows Multi-Services

1. Register (Workflow Complexe)

Actuel dans Auth :

typescript
// auth/auth0.service.ts - executeRegistration()
async executeRegistration(data: BaseRegistrationData) {
  // 1. Créer Auth0 user
  const auth0User = await this.managementClient.users.create(...);
  
  // 2. Créer local user
  const localUser = await this.usersService.create(...);
  
  // 3. Créer person dans Customer Service
  const customerResult = await this.customerHttpService.registerUser(...);
  
  // 4. Créer wallet dans Ledger Service
  const walletResult = await this.ledgerWalletHttpService.initiateWallet(...);
  
  // ❌ Pas de compensation en cas d'échec partiel
}

Problèmes :

  • ❌ Auth appelle directement Customer et Ledger (violation de l'architecture)
  • ❌ Pas de compensation (Saga pattern)
  • ❌ Logique métier dans le service Auth

Solution : Déplacer vers Orchestrator

typescript
// orchestrator/application/use-cases/auth/register-user.use-case.ts
@Injectable()
export class RegisterUserUseCase {
  constructor(
    @Inject(AUTH_SERVICE) private readonly authService: IAuthService,
    @Inject(CUSTOMER_SERVICE) private readonly customerService: ICustomerService,
    @Inject(LEDGER_SERVICE) private readonly ledgerService: ILedgerService,
  ) {}

  async execute(input: RegisterUserInput): Promise<RegisterUserResult> {
    let auth0User = null;
    let localUser = null;
    let person = null;
    let wallet = null;

    try {
      // 1. Créer Auth0 user
      auth0User = await this.authService.createAuth0User(...);
      
      // 2. Créer local user
      localUser = await this.authService.createLocalUser(...);
      
      // 3. Créer person dans Customer Service
      person = await this.customerService.registerUser(...);
      
      // 4. Créer wallet dans Ledger Service
      wallet = await this.ledgerService.initiateWallet(...);
      
      return { success: true, data: { ... } };
    } catch (error) {
      // Compensation (Saga)
      if (wallet) await this.ledgerService.deleteWallet(wallet.id);
      if (person) await this.customerService.deletePerson(person.id);
      if (localUser) await this.authService.deleteLocalUser(localUser.id);
      if (auth0User) await this.authService.deleteAuth0User(auth0User.id);
      
      throw error;
    }
  }
}

2. Reset PIN (Workflow Multi-Étapes)

Actuel dans Auth :

typescript
// auth/auth.controller.ts - confirmResetPin()
@Post('reset-pin/confirm')
async confirmResetPin(@Body() resetPinDto: ResetPinDto) {
  // 1. Vérifier OTP
  const otpResult = await this.otpService.verifyResetPinOtp(...);
  
  // 2. Get person from Customer Service
  const personResult = await this.auth0Service['customerHttpService'].getPersonByPhone(...);
  
  // 3. Find local user
  const user = await this.auth0Service['usersService']['userRepository'].findOne(...);
  
  // 4. Update PIN in Auth0
  await this.auth0Service['managementClient'].users.update(...);
  
  // 5. Update PIN in local DB
  await this.auth0Service['usersService'].update(...);
}

Problèmes :

  • ❌ Auth appelle directement Customer Service
  • ❌ Logique métier dans le controller
  • ❌ Pas de compensation si étape 4 échoue après étape 3

Solution : Déplacer vers Orchestrator

typescript
// orchestrator/application/use-cases/auth/reset-pin.use-case.ts
@Injectable()
export class ResetPinUseCase {
  constructor(
    @Inject(AUTH_SERVICE) private readonly authService: IAuthService,
    @Inject(CUSTOMER_SERVICE) private readonly customerService: ICustomerService,
  ) {}

  async execute(input: ResetPinInput): Promise<ResetPinResult> {
    // 1. Vérifier OTP (via Auth Service)
    await this.authService.verifyResetPinOtp(...);
    
    // 2. Get person from Customer Service
    const person = await this.customerService.getPersonByPhone(...);
    
    // 3. Reset PIN (via Auth Service)
    await this.authService.resetPin(person.userId, input.newPin);
    
    return { success: true };
  }
}

Architecture Cible

Auth Service (Responsabilités)

Auth Service
├── Authentification pure
│   ├── Login (username/password)
│   ├── Login (phone/PIN)
│   ├── Validate token
│   ├── Refresh token
│   └── Logout
├── Gestion OTP
│   ├── Send OTP
│   └── Verify OTP
└── Gestion PIN
    ├── Check PIN
    └── Change PIN (authentifié)

Interfaces exposées (pour Orchestrator) :

typescript
// domain/interfaces/services/auth.service.interface.ts
export interface IAuthService {
  // Authentification
  login(input: LoginInput): Promise<LoginResult>;
  validateToken(token: string): Promise<TokenValidationResult>;
  refreshToken(refreshToken: string): Promise<RefreshTokenResult>;
  logout(refreshToken: string): Promise<void>;
  
  // User management (pour Orchestrator)
  createAuth0User(input: CreateAuth0UserInput): Promise<Auth0User>;
  createLocalUser(input: CreateLocalUserInput): Promise<LocalUser>;
  deleteAuth0User(auth0Id: string): Promise<void>;
  deleteLocalUser(userId: string): Promise<void>;
  
  // PIN management
  checkPin(userId: string, pin: string): Promise<boolean>;
  changePin(userId: string, oldPin: string, newPin: string): Promise<void>;
  resetPin(userId: string, newPin: string): Promise<void>;
  
  // OTP (pour Orchestrator)
  verifyResetPinOtp(phone: string, code: string): Promise<boolean>;
}

Orchestrator Service (Nouveaux Use Cases)

Orchestrator Service
├── auth/
│   ├── register-user.use-case.ts          # Register complet
│   ├── register-merchant.use-case.ts     # Register merchant
│   ├── register-agent.use-case.ts        # Register agent
│   ├── register-admin.use-case.ts        # Register admin
│   └── reset-pin.use-case.ts              # Reset PIN workflow
├── organizations/
│   └── create-organization.use-case.ts
└── transactions/
    └── process-transfer.use-case.ts

Nouveaux endpoints Orchestrator :

POST /orchestrator/auth/register          # Remplace POST /auth/register
POST /orchestrator/auth/register/merchant # Remplace POST /auth/register/merchant
POST /orchestrator/auth/register/agent     # Remplace POST /auth/register/agent
POST /orchestrator/auth/register/admin     # Remplace POST /auth/register/admin
POST /orchestrator/auth/reset-pin          # Remplace POST /auth/reset-pin/confirm

Endpoints Auth qui restent :

POST /auth/login
POST /auth/login/phone-pin
GET  /auth/me
POST /auth/refresh
POST /auth/logout
POST /auth/send-otp
POST /auth/verify-otp
POST /auth/check-pin
POST /auth/change-pin
POST /auth/reset-pin/request              # Demande de reset (envoie OTP)
POST /auth/reset-pin/verify-otp           # Vérifie OTP (sans reset)

Plan de Migration

Phase 1 : Préparer Auth Service

  1. Créer interfaces pour les services Auth (IAuthService)
  2. Extraire méthodes utilisables par Orchestrator :
    • createAuth0User()
    • createLocalUser()
    • deleteAuth0User()
    • deleteLocalUser()
    • resetPin()
  3. Créer adapter HTTP dans Orchestrator pour appeler Auth

Phase 2 : Implémenter dans Orchestrator

  1. Créer RegisterUserUseCase dans Orchestrator
  2. Créer ResetPinUseCase dans Orchestrator
  3. Implémenter compensation (Saga pattern)
  4. Créer controllers dans Orchestrator

Phase 3 : Migrer les Endpoints

  1. Déplacer endpoints de Auth vers Orchestrator
  2. Garder endpoints Auth pour compatibilité (dépréciés)
  3. Mettre à jour API Gateway pour router vers Orchestrator
  4. Tester les workflows complets

Phase 4 : Nettoyer Auth Service

  1. Supprimer les appels directs à Customer/Ledger
  2. Supprimer les endpoints dépréciés
  3. Simplifier Auth0Service (ne garder que l'authentification)

Bénéfices de cette Architecture

✅ Conformité avec l'Architecture

  • Orchestrator est le seul service habilité à appeler les services métier
  • Auth se concentre uniquement sur l'authentification

✅ Compensation (Saga Pattern)

  • Rollback automatique en cas d'échec partiel
  • Cohérence des données garantie

✅ Réutilisabilité

  • Les workflows Register peuvent être réutilisés par d'autres services
  • Logique métier centralisée

✅ Testabilité

  • Use Cases isolés et testables
  • Mocks faciles pour les services externes

✅ Maintenabilité

  • Séparation claire des responsabilités
  • Code plus simple et lisible

Cas Particulier : Login Phone-PIN

Actuel :

typescript
// Auth appelle Customer pour résoudre le user par téléphone
const personResult = await this.customerHttpService.getPersonByPhone(...);

Options :

Option 1 : Rester dans Auth

Si getPersonByPhone est juste une lecture pour résoudre l'utilisateur, on peut laisser dans Auth.

Mais : Cela viole le principe "Customer n'est jamais appelé directement".

Option 2 : Déplacer vers Orchestrator

Créer un LoginWithPhonePinUseCase dans Orchestrator qui :

  1. Appelle Customer pour résoudre le user
  2. Appelle Auth pour vérifier le PIN
  3. Retourne les tokens

Recommandation : Option 2 pour respecter l'architecture, mais avec une exception possible si c'est juste une lecture simple.


Conclusion

OUI, il est pertinent et même nécessaire de déporter les workflows multi-services vers l'Orchestrator.

Cela permet de :

  • ✅ Respecter l'architecture documentée
  • ✅ Implémenter la compensation (Saga)
  • ✅ Centraliser la logique métier
  • ✅ Simplifier le service Auth

Le service Auth devient un service d'authentification pur, et l'Orchestrator gère tous les workflows métier multi-services.


Document créé le 22/01/2026


Annexe — Plan use-cases Auth côté Orchestrator

Contexte

Selon l'architecture documentée (auth-orchestrator-boundary.md), les workflows multi-services comme Register et Reset PIN doivent être orchestrés par l'Orchestrator Service.

État Actuel

  • Phase 1 Complétée : Auth est autonome pour le phone (plus de dépendance Customer pour login)
  • Phase 2 À Faire : Créer les use-cases dans Orchestrator
  • Phase 3 À Faire : Créer les endpoints internes dans Auth pour Orchestrator
  • Phase 4 À Faire : Migrer les endpoints publics

Plan d'Action

Phase 1 : Créer les Endpoints Internes dans Auth Service

Objectif : Exposer des méthodes granulaires pour permettre à Orchestrator d'orchestrer avec compensation.

Endpoints à Créer

POST   /auth/internal/create-auth0-user      # Crée uniquement Auth0 user
POST   /auth/internal/create-local-user      # Crée uniquement local user
DELETE /auth/internal/auth0-user/:id          # Supprime Auth0 user (compensation)
DELETE /auth/internal/local-user/:id          # Supprime local user (compensation)
POST   /auth/internal/reset-pin               # Reset PIN (avec OTP vérifié)
POST   /auth/internal/verify-reset-pin-otp    # Vérifie OTP pour reset PIN

Fichiers à créer/modifier :

  • services/auth/src/auth/internal-auth.controller.ts (nouveau)
  • services/auth/src/auth/internal-auth.service.ts (nouveau)
  • services/auth/src/auth/auth.module.ts (ajouter InternalAuthService)

Sécurité : Ces endpoints doivent être protégés et accessibles uniquement depuis Orchestrator (via réseau interne ou API key).


Phase 2 : Mettre à Jour AuthHttpAdapter dans Orchestrator

Fichier : services/orchestrator/src/infrastructure/adapters/http/auth-http.adapter.ts

Méthodes à ajouter :

  • createAuth0User()
  • createLocalUser()
  • deleteAuth0User()
  • deleteLocalUser()
  • resetPin()
  • verifyResetPinOtp()

Phase 3 : Créer les Use-Cases dans Orchestrator

3.1 RegisterUserUseCase

Fichier : services/orchestrator/src/application/use-cases/auth/register-user.use-case.ts

Workflow :

  1. Créer Auth0 user
  2. Créer local user
  3. Créer person + profile dans Customer
  4. Créer wallet dans Ledger
  5. Envoyer notification

Compensation : Rollback dans l'ordre inverse si échec

3.2 RegisterMerchantUseCase

Fichier : services/orchestrator/src/application/use-cases/auth/register-merchant.use-case.ts

Workflow : Similaire à RegisterUserUseCase + création merchant

3.3 RegisterAgentUseCase

Fichier : services/orchestrator/src/application/use-cases/auth/register-agent.use-case.ts

Workflow : Similaire à RegisterUserUseCase + création agent

3.4 RegisterAdminUseCase

Fichier : services/orchestrator/src/application/use-cases/auth/register-admin.use-case.ts

Workflow : Similaire à RegisterUserUseCase (sans wallet)

3.5 ResetPinUseCase

Fichier : services/orchestrator/src/application/use-cases/auth/reset-pin.use-case.ts

Workflow :

  1. Vérifier OTP (via Auth Service)
  2. Trouver user par phone (via Auth Service)
  3. Reset PIN dans Auth0
  4. Reset PIN dans local DB

Compensation : Pas nécessaire (opération atomique)


Phase 4 : Créer les Controllers dans Orchestrator

Fichier : services/orchestrator/src/presentation/controllers/auth.controller.ts

Endpoints :

POST /orchestrator/auth/register          # Register user
POST /orchestrator/auth/register/merchant # Register merchant
POST /orchestrator/auth/register/agent     # Register agent
POST /orchestrator/auth/register/admin     # Register admin
POST /orchestrator/auth/reset-pin          # Reset PIN

Phase 5 : Mettre à Jour les Modules

Fichiers :

  • services/orchestrator/src/modules/auth/auth.module.ts (nouveau)
  • services/orchestrator/src/app.module.ts (importer AuthModule)

Ordre d'Exécution Recommandé

  1. Créer endpoints internes dans Auth (Phase 1)
  2. Mettre à jour AuthHttpAdapter (Phase 2)
  3. Créer RegisterUserUseCase (Phase 3.1)
  4. Créer ResetPinUseCase (Phase 3.5)
  5. Créer les autres Register use-cases (Phase 3.2-3.4)
  6. Créer controllers (Phase 4)
  7. Mettre à jour modules (Phase 5)
  8. Tester les workflows complets

Détails Techniques

Endpoints Internes Auth

Ces endpoints doivent être :

  • Protégés : Accessibles uniquement depuis Orchestrator
  • Granulaires : Une opération = un endpoint
  • Idempotents : Peuvent être appelés plusieurs fois sans effet de bord

Compensation (Saga Pattern)

Chaque use-case doit implémenter une méthode compensate() qui :

  • Rollback dans l'ordre inverse de création
  • Log les erreurs de compensation
  • Alerte l'équipe ops si compensation échoue

Gestion d'Erreurs

  • Utiliser les domain exceptions existantes
  • Propager les erreurs avec contexte
  • Logger toutes les erreurs pour debugging

Prochaines Étapes

  1. Interface IAuthService étendue (fait)
  2. Créer endpoints internes dans Auth (à faire)
  3. Mettre à jour AuthHttpAdapter (à faire)
  4. Créer les use-cases (à faire)
  5. Créer les controllers (à faire)

Plan créé le 22/01/2026

NxPay — Plateforme fintech CEMAC