DEV NOTE

Quand (et comment) adopter une architecture hexagonale

Quand et comment isoler le cœur métier des bases de données, APIs et interfaces avec une architecture hexagonale.

Introduction : Ports & Adapters

L'architecture hexagonale (Alistair Cockburn) sépare logique métier du monde extérieur. Objectif : Code métier indépendant de l'infrastructure.

Les 3 couches

Domain (cœur métier)

  • Règles métier pures
  • Entités, Value Objects
  • Aucune dépendance externe

Application (use cases)

  • Orchestration de la logique métier
  • Définit les ports (interfaces)

Infrastructure (adapters)

  • Implémentations concrètes
  • DB, API, UI, Email...

Structure projet

src/
 ├── domain/
 │   ├── entities/
 │   │   └── User.ts
 │   └── value-objects/
 │       └── Email.ts
 ├── application/
 │   ├── ports/
 │   │   ├── UserRepository.ts  (interface)
 │   │   └── EmailService.ts    (interface)
 │   └── use-cases/
 │       └── CreateUser.ts
 └── infrastructure/
     ├── adapters/
     │   ├── PostgresUserRepository.ts
     │   └── SendGridEmailService.ts
     └── http/
         └── UserController.ts

Exemple concret

Domain

// domain/entities/User.ts
export class User {
  constructor(
    public id: string,
    public email: string,
    public name: string
  ) {}
}

Application (Port)

// application/ports/UserRepository.ts
export interface UserRepository {
  save(user: User): Promise<void>;
  findById(id: string): Promise<User | null>;
}

Application (Use Case)

// application/use-cases/CreateUser.ts
export class CreateUser {
  constructor(private userRepo: UserRepository) {}
  
  async execute(email: string, name: string) {
    const user = new User(uuid(), email, name);
    await this.userRepo.save(user);
    return user;
  }
}

Infrastructure (Adapter)

// infrastructure/adapters/PostgresUserRepository.ts
export class PostgresUserRepository implements UserRepository {
  async save(user: User) {
    await db.query('INSERT INTO users...', [user.id, user.email]);
  }
  
  async findById(id: string) {
    const row = await db.query('SELECT * FROM users WHERE id = $1', [id]);
    return row ? new User(row.id, row.email, row.name) : null;
  }
}

Avantages

  • Tests faciles : Mock repositories sans vraie DB
  • Flexibilité : Changer de DB/API sans toucher métier
  • Longévité : Code métier stable même si infra change

Quand l'utiliser ?

Oui si :
  • Logique métier complexe
  • Projet long terme (5+ ans)
  • Besoin de tests robustes
  • Infrastructure susceptible de changer
Non si :
  • CRUD simple
  • Prototype / MVP rapide
  • Équipe junior (courbe d'apprentissage)

Conclusion

Architecture hexagonale = séparation "quoi" (métier) et "comment" (infra). Idéale pour projets évolutifs avec logique métier riche. > "Le métier ne devrait jamais dépendre de la technique."