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 :
| Workflow | Endpoint | Services appelés | Action |
|---|---|---|---|
| Login | POST /auth/login | Auth0 uniquement | ✅ Reste dans Auth |
| Login Phone-PIN | POST /auth/login/phone-pin | Auth0 + Customer (⚠️) | ⚠️ Voir analyse ci-dessous |
| Validate Token | GET /auth/me | Auth0 uniquement | ✅ Reste dans Auth |
| Refresh Token | POST /auth/refresh | Auth0 uniquement | ✅ Reste dans Auth |
| Logout | POST /auth/logout | Auth0 + Redis uniquement | ✅ Reste dans Auth |
| Send OTP | POST /auth/send-otp | Redis uniquement | ✅ Reste dans Auth |
| Verify OTP | POST /auth/verify-otp | Redis uniquement | ✅ Reste dans Auth |
| Check PIN | POST /auth/check-pin | Auth0 uniquement | ✅ Reste dans Auth |
| Change PIN | POST /auth/change-pin | Auth0 uniquement | ✅ Reste dans Auth |
2. Workflows Multi-Services (DÉPLACER vers Orchestrator) ❌
Ces workflows appellent plusieurs services métier :
| Workflow | Endpoint | Services appelés | Action |
|---|---|---|---|
| Register | POST /auth/register | Auth0 + Local DB + Customer + Ledger | ❌ Déplacer vers Orchestrator |
| Register Merchant | POST /auth/register/merchant | Auth0 + Local DB + Customer + Ledger | ❌ Déplacer vers Orchestrator |
| Register Agent | POST /auth/register/agent | Auth0 + Local DB + Customer + Ledger | ❌ Déplacer vers Orchestrator |
| Register Admin | POST /auth/register/admin | Auth0 + Local DB + Customer | ❌ Déplacer vers Orchestrator |
| Reset PIN | POST /auth/reset-pin/confirm | OTP + Customer + Auth0 | ❌ Déplacer vers Orchestrator |
3. Workflows Ambigus (À Analyser) ⚠️
| Workflow | Endpoint | Services appelés | Analyse |
|---|---|---|---|
| Login Phone-PIN | POST /auth/login/phone-pin | Auth0 + 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 :
// 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
// 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 :
// 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
// 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) :
// 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.tsNouveaux 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/confirmEndpoints 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
- Créer interfaces pour les services Auth (IAuthService)
- Extraire méthodes utilisables par Orchestrator :
createAuth0User()createLocalUser()deleteAuth0User()deleteLocalUser()resetPin()
- Créer adapter HTTP dans Orchestrator pour appeler Auth
Phase 2 : Implémenter dans Orchestrator
- Créer
RegisterUserUseCasedans Orchestrator - Créer
ResetPinUseCasedans Orchestrator - Implémenter compensation (Saga pattern)
- Créer controllers dans Orchestrator
Phase 3 : Migrer les Endpoints
- Déplacer endpoints de Auth vers Orchestrator
- Garder endpoints Auth pour compatibilité (dépréciés)
- Mettre à jour API Gateway pour router vers Orchestrator
- Tester les workflows complets
Phase 4 : Nettoyer Auth Service
- Supprimer les appels directs à Customer/Ledger
- Supprimer les endpoints dépréciés
- 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 :
// 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 :
- Appelle Customer pour résoudre le user
- Appelle Auth pour vérifier le PIN
- 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 PINFichiers à 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 :
- Créer Auth0 user
- Créer local user
- Créer person + profile dans Customer
- Créer wallet dans Ledger
- 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 :
- Vérifier OTP (via Auth Service)
- Trouver user par phone (via Auth Service)
- Reset PIN dans Auth0
- 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 PINPhase 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é
- Créer endpoints internes dans Auth (Phase 1)
- Mettre à jour AuthHttpAdapter (Phase 2)
- Créer RegisterUserUseCase (Phase 3.1)
- Créer ResetPinUseCase (Phase 3.5)
- Créer les autres Register use-cases (Phase 3.2-3.4)
- Créer controllers (Phase 4)
- Mettre à jour modules (Phase 5)
- 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
- ✅ Interface IAuthService étendue (fait)
- ⏳ Créer endpoints internes dans Auth (à faire)
- ⏳ Mettre à jour AuthHttpAdapter (à faire)
- ⏳ Créer les use-cases (à faire)
- ⏳ Créer les controllers (à faire)
Plan créé le 22/01/2026