Orchestrator — migration et plan de refactoring
Structure actuelle vs cible, analyse détaillée des écarts, plan de refactoring, checklist de conformité, priorités recommandées et FAQ. Pour la vue d'ensemble, voir Orchestrator — overview.
Migration Progressive
Étape 1 : Créer l'Orchestrator Service
- Scaffold le service
- Implémenter les adapters HTTP vers les services existants
- Implémenter
CreateOrganizationUseCase
Étape 2 : Router via API Gateway
- Configurer le routing vers l'Orchestrator
- Tester le workflow complet
Étape 3 : Fermer les accès directs
- Retirer l'exposition publique des services métier
- Configurer le réseau Docker
Étape 4 : Migrer les autres workflows
- Identifier tous les workflows multi-services
- Les migrer un par un vers l'Orchestrator
Questions Fréquentes
Pourquoi pas de message broker (Kafka/RabbitMQ) ?
Pour l'instant, la communication synchrone HTTP est suffisante et plus simple à debugger. On pourra migrer vers des events pour les workflows qui nécessitent une résilience accrue (ex: notifications asynchrones).
Comment gérer les échecs partiels ?
Le pattern Saga avec compensation : on annule les opérations réussies dans l'ordre inverse si une étape échoue.
Performance ?
L'Orchestrator ajoute un hop réseau. C'est acceptable pour les workflows de création. Pour les lectures simples (get wallet balance), on peut autoriser un accès direct via l'API Gateway.
Document à présenter à l'équipe - Version 1.0
Annexe — Audit architecture Orchestrator
Résumé Exécutif
| Critère | Status | Note |
|---|---|---|
| Interfaces définies | ✅ Bon | Interfaces pour tous les services externes |
| Injection de dépendances | ✅ Bon | Tokens utilisés correctement |
| Séparation des responsabilités | ⚠️ Partiel | Orchestrators existent mais mal positionnés |
| Clean Architecture Layers | ❌ Non conforme | Pas de séparation domain/application/infrastructure |
| Compensation (Saga) | ❌ Absent | Pas de rollback en cas d'échec |
| Naming conventions | ⚠️ Partiel | Orchestrators ≠ Use Cases |
Score global : 50% - Bonnes fondations, restructuration nécessaire.
Structure Actuelle vs Cible
Actuelle
src/
├── core/
│ ├── common/ # ✅ OK
│ ├── config/ # ✅ OK
│ ├── dto/ # ⚠️ Mal placé
│ ├── interfaces/ # ⚠️ Devrait être dans domain/
│ └── services/ # ⚠️ Devrait être dans infrastructure/
├── modules/
│ └── transactions/
│ ├── controllers/ # ✅ OK (mais devrait être dans presentation/)
│ ├── dto/ # ⚠️ Mélange request/response/internal
│ ├── orchestrators/ # ⚠️ Devrait être use-cases dans application/
│ └── services/ # ⚠️ Application services mal positionnés
└── app.module.tsCible
src/
├── core/ # Config, constants, tokens
│ ├── config/
│ └── constants/
│ └── injection-tokens.ts
├── common/ # Cross-cutting concerns
│ ├── decorators/
│ ├── filters/
│ ├── interceptors/
│ └── middleware/
├── domain/ # COUCHE DOMAIN
│ ├── interfaces/
│ │ └── services/ # ILedgerService, ICustomerService...
│ └── exceptions/ # Domain exceptions
├── application/ # COUCHE APPLICATION
│ ├── use-cases/
│ │ ├── transactions/
│ │ │ ├── process-transfer.use-case.ts
│ │ │ └── process-recharge.use-case.ts
│ │ └── organizations/
│ │ └── create-organization.use-case.ts
│ └── services/ # Application services partagés
│ ├── account-resolution.service.ts
│ ├── fees-calculation.service.ts
│ └── transaction-validation.service.ts
├── infrastructure/ # COUCHE INFRASTRUCTURE
│ └── adapters/
│ └── http/
│ ├── base-http.adapter.ts
│ ├── ledger-http.adapter.ts
│ ├── customer-http.adapter.ts
│ └── notification-http.adapter.ts
├── presentation/ # COUCHE PRESENTATION
│ ├── controllers/
│ │ └── transactions.controller.ts
│ └── dto/
│ ├── requests/
│ └── responses/
└── modules/ # Composition DI
└── transactions.module.tsAnalyse Détaillée
1. Points Positifs ✅
1.1 Interfaces bien définies
// core/interfaces/ledger-wallet.interface.ts
export interface ILedgerWalletService {
updateWallet(request: WalletUpdateRequestDto): Promise<WalletUpdateResponseDto>;
getWalletByUserId(userId: string): Promise<{ accountId: string; balance?: number }>;
createP2PTransaction(request: any): Promise<{ id: string; balance?: number }>;
}Verdict : Les interfaces sont présentes et permettent le découplage.
1.2 Injection de dépendances via tokens
// transfer.orchestrator.ts
constructor(
@Inject('LEDGER_WALLET_SERVICE')
private readonly ledgerWalletClient: ILedgerWalletService,
@Inject('RISK_ENGINE_SERVICE')
private readonly riskEngineClient: IRiskEngineService,
)Verdict : Pattern correct, permet de changer d'implémentation facilement.
1.3 Services applicatifs spécialisés
AccountResolutionService- Résolution des comptesTransactionValidationService- Validations métierFeesCalculationService- Calcul des fraisTransactionMapperService- Mapping des données
Verdict : Bonne séparation des responsabilités.
2. Points à Améliorer ⚠️
2.1 Orchestrator vs Use Case
Problème : Les "orchestrators" font le travail des "use cases" mais :
- Sont dans
modules/transactions/orchestrators/au lieu deapplication/use-cases/ - Ne suivent pas le naming convention
*.use-case.ts
Impact : Confusion sur les responsabilités, non-conformité avec le CLAUDE.md.
Solution :
# Renommer et déplacer
modules/transactions/orchestrators/transfer.orchestrator.ts
→ application/use-cases/transactions/process-transfer.use-case.ts2.2 Controller → Service → Orchestrator (couche inutile)
Problème : Le controller passe par TransactionsService qui délègue à l'orchestrator.
// Actuel : Controller → Service → Orchestrator
@Post('send')
async send(@Body() dto) {
return this.transactionsService.send(dto, ...); // Couche intermédiaire
}
// transactionsService.send() appelle juste transferOrchestrator.execute()Impact : Couche TransactionsService ajoute de la complexité sans valeur.
Solution : Controller appelle directement le Use Case.
// Cible : Controller → Use Case
@Post('send')
async send(@Body() dto) {
return this.processTransferUseCase.execute(dto, ...);
}2.3 DTOs mal organisés
Problème :
core/dto/contient des DTOs internesmodules/transactions/dto/mélange request, response, et internal
Solution :
presentation/dto/requests/send-transaction.dto.ts # Request du client
presentation/dto/responses/send-response.dto.ts # Response au client
application/dto/internal/transaction-request.dto.ts # DTOs internes3. Points Critiques ❌
3.1 Pas de couche Domain
Problème : Aucune entité de domaine, value objects, ou exceptions métier.
Impact :
- Logique métier dispersée dans les services
- Validation métier difficile à maintenir
- Pas de richesse comportementale des entités
Solution : Créer la couche domain avec :
// domain/exceptions/insufficient-balance.exception.ts
export class InsufficientBalanceException extends DomainException {
readonly code = 'INSUFFICIENT_BALANCE';
readonly statusCode = 400;
}
// domain/exceptions/transaction-blocked.exception.ts
export class TransactionBlockedException extends DomainException {
readonly code = 'TRANSACTION_BLOCKED';
readonly statusCode = 403;
}3.2 Pas de compensation (Saga Pattern)
Problème : L'orchestrator Transfer ne gère pas les échecs partiels.
// Actuel - Pas de rollback
async execute(dto, ...) {
// Si une étape échoue après la vérification PIN,
// aucune compensation n'est effectuée
const pinVerification = await this.authClient.verifyPin(...);
const [senderContext, receiverContext] = await Promise.all([...]);
// ... Si ça échoue ici, pas de rollback
const transaction = await this.ledgerWalletClient.createP2PTransaction(...);
}Impact : En cas d'échec partiel, données incohérentes possibles.
Solution : Implémenter le pattern Saga avec compensation.
async execute(input) {
let createdTransaction = null;
try {
// Steps...
createdTransaction = await this.ledgerService.createTransaction(...);
// ...
} catch (error) {
// Compensation
if (createdTransaction) {
await this.ledgerService.reverseTransaction(createdTransaction.id);
}
throw error;
}
}3.3 Adapters HTTP dans le mauvais dossier
Problème : core/services/ledger-wallet/ledger-wallet-http.service.ts
Solution : Déplacer vers infrastructure/adapters/http/ledger-http.adapter.ts
3.4 Interfaces dans core au lieu de domain
Problème : core/interfaces/ devrait être domain/interfaces/services/
Plan de Refactoring
Phase 1 : Structure des dossiers (sans casser le code)
# 1. Créer la nouvelle structure
mkdir -p src/domain/interfaces/services
mkdir -p src/domain/exceptions
mkdir -p src/application/use-cases/transactions
mkdir -p src/application/use-cases/organizations
mkdir -p src/application/services
mkdir -p src/infrastructure/adapters/http
mkdir -p src/presentation/controllers
mkdir -p src/presentation/dto/requests
mkdir -p src/presentation/dto/responses
# 2. Déplacer les interfaces (avec alias temporaires)
mv src/core/interfaces/*.interface.ts src/domain/interfaces/services/
# 3. Déplacer les adapters HTTP
mv src/core/services/*/*.service.ts src/infrastructure/adapters/http/
# Renommer en *.adapter.ts
# 4. Déplacer les orchestrators → use-cases
mv src/modules/transactions/orchestrators/*.orchestrator.ts src/application/use-cases/transactions/
# Renommer en *.use-case.tsPhase 2 : Renommage et refactoring
| Fichier Actuel | Fichier Cible |
|---|---|
transfer.orchestrator.ts | process-transfer.use-case.ts |
recharge.orchestrator.ts | process-recharge.use-case.ts |
fees.orchestrator.ts | calculate-fees.use-case.ts |
ledger-wallet-http.service.ts | ledger-http.adapter.ts |
customer-http.service.ts | customer-http.adapter.ts |
Phase 3 : Ajouter Domain Exceptions
// domain/exceptions/base.exception.ts
export abstract class DomainException extends Error {
abstract readonly code: string;
abstract readonly statusCode: number;
}
// domain/exceptions/insufficient-balance.exception.ts
// domain/exceptions/transaction-blocked.exception.ts
// domain/exceptions/wallet-not-found.exception.ts
// domain/exceptions/invalid-pin.exception.tsPhase 4 : Ajouter Compensation (Saga)
Modifier les use-cases pour gérer les rollbacks.
Checklist de Conformité
| Règle | Actuel | Action |
|---|---|---|
Interfaces dans domain/interfaces/services/ | ❌ | Déplacer |
Use Cases dans application/use-cases/ | ❌ | Déplacer + renommer |
Adapters HTTP dans infrastructure/adapters/http/ | ❌ | Déplacer + renommer |
Controllers dans presentation/controllers/ | ⚠️ | Déplacer |
| DTOs séparés (requests/responses) | ❌ | Réorganiser |
| Domain Exceptions | ❌ | Créer |
| Compensation (Saga) | ❌ | Implémenter |
| Injection tokens centralisés | ⚠️ | Consolider dans core/constants/ |
| Suffixes de fichiers corrects | ⚠️ | Renommer |
Priorités Recommandées
- P0 - Critique : Ajouter compensation dans les use-cases existants
- P1 - Important : Créer domain exceptions + les utiliser
- P2 - Moyen : Restructurer les dossiers
- P3 - Low : Renommer les fichiers
Audit réalisé le 22/01/2026