DDD sur TypeScript : Architecture propre pour les projets complexes

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi le CRUD ordinaire tue votre projet, et comment le DDD peut y remédier

\n\n

Chaque développeur a déjà été confronté à une situation où un projet, simple à première vue, se transforme en un « marécage » de services, de contrôleurs et de logique métier dispersée dans tout le code. Vous commencez à écrire UserService, puis OrderService, et six mois plus tard, OrderService contient la logique d'envoi d'email, de calcul des remises et de vérification du solde utilisateur. Cela vous dit quelque chose ?

\n\n

Le problème est que nous pensons trop tôt aux bases de données, aux frameworks et aux API REST. Nous oublions l'essentiel — le domaine métier (domain). Le Domain-Driven Design (DDD) propose une approche radicalement différente : d'abord, nous modélisons les processus métier dans le langage des experts, et ensuite seulement, le code.

\n\n

Dans cet article, nous allons voir comment appliquer le Domain-Driven Design en TypeScript avec des exemples concrets. Vous découvrirez comment construire une architecture propre où les règles métier vivent séparément de l'infrastructure, et où le code est facile à tester et à modifier.

\n\n

Fondamentaux du DDD : langage, contextes et modèle

\n\n

Ubiquitous Language (Langage omniprésent)

\n

Le premier principe du DDD est un langage commun, compréhensible à la fois par les développeurs et les experts métier. Si le manager dit « Le client a activé son abonnement », alors dans le code, il doit y avoir une méthode activateSubscription(), et non setStatus(1). TypeScript, avec son typage strict, est idéal pour cela — les noms des classes, méthodes et variables deviennent une documentation.

\n\n

Bounded Context (Contexte délimité)

\n

Un grand système est divisé en contextes indépendants : « Paiements », « Gestion des utilisateurs », « Livraison ». Chaque contexte a son propre modèle et son propre langage. Les modules TypeScript et les types stricts aident à séparer rigoureusement ces contextes au niveau des imports.

\n\n

Construire un modèle domaine en TypeScript

\n\n

Value Object (Objet-valeur)

\n

Il s'agit d'une entité immuable définie par ses attributs, et non par un identifiant. Exemple : Email, Money, Coordinates.

\n\n
// value-objects/Email.ts\nexport class Email {\n  private readonly value: string;\n\n  constructor(email: string) {\n    if (!this.isValid(email)) {\n      throw new Error('Invalid email address');\n    }\n    this.value = email;\n  }\n\n  private isValid(email: string): boolean {\n    return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n  }\n\n  public getValue(): string {\n    return this.value;\n  }\n\n  public equals(other: Email): boolean {\n    return this.value === other.value;\n  }\n}\n
\n\n

Remarquez : Email vérifie lui-même sa validité. Aucun service ne doit savoir à quoi ressemble un email valide. C'est du Domain-Driven Design en TypeScript pur — la règle métier vit à l'intérieur de l'objet.

\n\n

Entity (Entité)

\n

Contrairement au Value Object, une entité possède un identifiant (ID) et peut évoluer dans le temps. Exemple : User, Order.

\n\n
// entities/User.ts\nimport { Email } from '../value-objects/Email';\nimport { UserId } from '../value-objects/UserId';\n\nexport class User {\n  private readonly id: UserId;\n  private email: Email;\n  private isActive: boolean;\n\n  constructor(id: UserId, email: Email) {\n    this.id = id;\n    this.email = email;\n    this.isActive = true;\n  }\n\n  public getId(): UserId {\n    return this.id;\n  }\n\n  public changeEmail(newEmail: Email): void {\n    if (!this.isActive) {\n      throw new Error('Cannot change email for inactive user');\n    }\n    this.email = newEmail;\n  }\n\n  public deactivate(): void {\n    this.isActive = false;\n  }\n}\n
\n\n

Aggregate (Agrégat)

\n

Un agrégat est un groupe d'entités et d'objets-valeurs traités comme un tout. L'agrégat a une racine (Aggregate Root) par laquelle l'accès aux objets internes s'effectue.

\n\n

Exemple : une commande (Order)

Blogs

Book Recommendations