Aller au contenu

ddd-ts-kit

Domain-Driven Design (DDD), Clean Architecture (hexagonale, ports et adaptateurs) et Clean Code pour les backends TypeScript et Node.js, tenus par des outils plutôt que par de bonnes intentions, pour les développeurs et pour les agents de code avec lesquels ils travaillent : Claude Code, Codex et GitHub Copilot.

Le Domain-Driven Design et la Clean Architecture s'érodent un raccourci à la fois : un new Date() dans un gestionnaire, un import entre deux contextes, une règle vérifiée dans un contrôleur. Sous la pression d'une échéance, et plus encore avec un agent d'IA qui écrit cent fichiers à l'heure, une consigne dans un wiki ne tient pas. ddd-ts-kit fait de l'architecture des règles à identifiant, et les vérifie à chaque écriture de fichier, avant chaque fusion et à chaque push.

  • Des règles, pas des consignes. Chaque règle a un identifiant, une raison et une correction (npx ddd-ts-kit explain <rule-id>), vérifiées par une CLI sans dépendance, un plugin ESLint et dependency-cruiser.
  • Générer, pas taper. ddd-ts-kit generate écrit 36 types de briques avec leur spec, leur fake et leur enregistrement, et laisse un TODO(domain) là où une décision métier est attendue.
  • Fait pour les agents de code. Un plugin pour Claude Code, Codex et GitHub Copilot : des skills qui conduisent le travail étape par étape, des agents pour le travail en parallèle et la revue, et un hook qui refuse une écriture qui enfreint une règle.
  • Votre stack, pas la nôtre. Express, Fastify, Hono ou NestJS ; zod, class-validator, TypeBox ou toute bibliothèque Standard Schema ; tsyringe, NestJS ou un câblage manuel ; Drizzle, Knex, Prisma ou TypeORM ; RabbitMQ, Kafka, NATS JetStream ou Redis Streams ; JWT ou Keycloak ; rôles, attributs ou relations.
  • Les exigences de la production, intégrées. Outbox transactionnelle et consommateurs idempotents, une permission par cas d'usage, journal d'audit, OpenTelemetry, multitenancy, contrats OpenAPI et AsyncAPI, un pipeline de CI pour GitHub et GitLab.
  • Prêt à être découpé. Chaque contexte délimité possède ses données et parle aux autres par des événements : il peut tourner comme son propre service le jour où l'équipe en a besoin.

L'histoire derrière le projet, sur Medium : Votre IA code vite. Votre architecture décide si ça tiendra. (en anglais).

La documentation est aussi un site web, avec une recherche, en français et en anglais : https://gbourgeat.github.io/ddd-ts-kit/fr/. Chaque page a sa traduction à côté d'elle, à laquelle mène le lien de sa première ligne.

Fenêtre de terminal
mkdir orders-api && cd orders-api
npx ddd-ts-kit init
pnpm install
pnpm run verify # types, lint, format, tests, e2e, architecture, graphe des imports, code mort, dérive de la documentation

init sans option prend Express, zod, tsyringe, Drizzle sur PostgreSQL, Vitest et pnpm, que corepack fournit à la version qu'épingle le package.json (corepack enable, une fois par machine). Le projet est vert dès ce premier verify, avant une ligne de code métier, et le reste à chaque étape. Ensuite, le langage omniprésent d'abord, le code ensuite :

Fenêtre de terminal
pnpm exec ddd-ts-kit generate context ordering --classification core \
--term "Order=A customer's request to buy products" --term "Place=To submit an order for fulfilment"
pnpm exec ddd-ts-kit generate context billing --classification supporting --upstream ordering:customer-supplier
pnpm exec ddd-ts-kit generate aggregate ordering Order
pnpm exec ddd-ts-kit generate repository ordering Order
pnpm exec ddd-ts-kit generate command ordering PlaceOrder --permission order.place --grant customer --aggregate Order
pnpm exec ddd-ts-kit generate route ordering PlaceOrder --method POST --path /orders
pnpm run docs:generate # le contrat OpenAPI suit la nouvelle route
pnpm run verify
pnpm exec ddd-ts-kit status # ce qui reste : TODO(domain), TODO(platform) et it.todo par contexte

Ces deux blocs sont exécutés tels qu'ils sont écrits, ligne par ligne, par la suite de bout en bout de ce dépôt, sur pnpm et sur npm. Avec npx ddd-ts-kit init --package-manager npm, les mêmes lignes se lisent npm install, npm run <script> et npx ddd-ts-kit <command>. Pour voir l'application tourner : cp .env.example .env, pnpm run infra:up (PostgreSQL, dans Docker), pnpm run db:generate initial, pnpm run db:migrate, puis pnpm run dev (rechargement automatique ; documentation de l'API sur /docs).

Un nom absent du glossaire du contexte est refusé, tout comme une commande ou une requête qui ne déclare ni permission ni --public. Une tranche, de bout en bout suit la requête HTTP POST /orders à travers les quatre couches et montre le code une fois les endroits TODO(domain) remplis.

Suivez ces étapes dans l'ordre : c'est le chemin pris en charge, et les outils sont faits pour le tenir. Chaque étape est une skill du plugin (/ddd-ts-kit:<skill>), ou une commande, et laisse un fichier que la suivante lit. /ddd-ts-kit:start conduit les étapes 1 à 7 une à une, et npx ddd-ts-kit status dit à tout moment quelle étape vient ensuite.

# Étape Skill ou commande Produit
1 Découvrir le domaine discover-domain docs/architecture/discovery.md : acteurs, commandes, événements, politiques
2 Trouver les contextes délimités bounded-contexts docs/architecture/contexts.md : chaque frontière et sa raison
3 Écrire les glossaires glossary le langage omniprésent de chaque contexte
4 Dessiner la carte des contextes context-map qui est en amont de qui, et selon quel patron
5 Choisir la stack et démarrer bootstrap, ddd-ts-kit init, generate context le projet, vert sur verify, un CONTEXT.md par contexte
6 Planifier les tranches plan-slices docs/architecture/slices.md : un cas d'usage par tranche, dans l'ordre
7 Construire tranche par tranche use-case (parallel-work pour plusieurs workers) le code et ses tests, un commit par tranche
8 Passer la porte de qualité quality-gate, npm run verify une porte verte avant chaque fusion
9 Documenter document, npm run docs:generate des contrats, une carte des contextes et des enregistrements de décision à jour

Les neuf étapes : quatre écrites dans les mots du métier sous docs/architecture/ (découvrir le domaine, trouver les contextes, écrire les glossaires, dessiner la carte des contextes), puis cinq avec les générateurs (démarrer, planifier les tranches, construire une tranche, passer la porte, documenter) ; les étapes 7 et 8 se répètent pour chaque tranche

La méthodologie est la référence de chaque étape et dit ce que coûte d'en sauter une ; quelle commande, quand donne la commande de chaque moment d'un projet.

Un gestionnaire écrit à la va-vite enfreint quatre règles d'un coup, et chaque constat nomme sa règle :

// application/commands/place-order/place-order.handler.ts, écrit à la va-vite
import { CommandHandler } from '@ddd-ts-kit/kernel/declarations';
import { PlaceOrderCommand } from '@/contexts/ordering/application/commands/place-order/place-order.command';
import { db, orders } from '@/contexts/ordering/infrastructure/database'; // layer/application-inward
@CommandHandler(PlaceOrderCommand)
export class PlaceOrderHandler implements CommandHandler<PlaceOrderCommand> {
async execute({ customerId, productIds }: PlaceOrderCommand): Promise<string> {
if (productIds.length === 0) {
throw new Error('Empty order'); // domain/no-generic-error
}
const id = crypto.randomUUID(); // purity/no-ambient-randomness
await db.insert(orders).values({ id, customerId, productIds, placedAt: new Date() }); // purity/no-system-clock
return id;
}
}
Au lieu de Écrivez Règle
new Date() this.clock.now(), le port Clock reçu dans le constructeur purity/no-system-clock
crypto.randomUUID() this.ids.next(), le port IdGenerator purity/no-ambient-randomness
throw new Error('Empty order') throw OrderError.empty(), un refus avec un code stable domain/no-generic-error
un import de infrastructure/ dans un gestionnaire le port OrderRepository, lié à son adaptateur par la plateforme layer/application-inward
un import du domain/ d'un autre contexte le published-language/ de ce contexte, ou un événement d'intégration context/published-language-only

Le même cas d'usage écrit dans les règles se trouve dans Une tranche, de bout en bout. Les règles couvrent, entre autres :

  • Couches et contextes (layer/*, context/*, tree/*) : le domaine n'importe rien d'autre que lui-même et le kernel ; un autre contexte n'est atteint que par son langage publié ; aucun cycle.
  • Langage omniprésent (language/*) : les classes du domaine et les cas d'usage sont nommés avec les termes du glossaire du contexte.
  • DDD tactique et pureté (domain/*, application/*, events/*, purity/*) : des agrégats encapsulés, des objets-valeurs immuables, des erreurs spécifiques, le temps et les identifiants par des ports.
  • Ports et câblage (ports/*, declarations/*, composition/*) : un port est une classe abstraite et son propre jeton d'injection, assemblé en un seul endroit.
  • Contrôle d'accès (authz/*) : chaque cas d'usage déclare sa permission ou dit qu'il est public, décidée dans la couche application, avec un acteur explicite.
  • Clean code et tests (clean/*, test/*) : de petites fonctions en ordre d'escalier, aucune échappatoire, une spec pour chaque unité de comportement, aucun mock.

npx ddd-ts-kit rules liste chaque identifiant avec sa sévérité ; la référence de l'architecture les définit.

Les mêmes règles vérifiées à trois moments : à chaque écriture de fichier par le hook du plugin Claude Code, avant chaque fusion par npm run verify, et à chaque push par le pipeline de CI, les trois appliquant les règles définies dans docs/ARCHITECTURE.md

Ce dépôt est la marketplace du plugin pour Claude Code, Codex et GitHub Copilot :

Agent Installation Une skill s'appelle
Claude Code /plugin marketplace add gbourgeat/ddd-ts-kit, puis /plugin install ddd-ts-kit@ddd-ts-kit /ddd-ts-kit:start
Codex codex plugin marketplace add gbourgeat/ddd-ts-kit, puis installer ddd-ts-kit depuis /plugins et approuver ses hooks dans /hooks $start
Copilot CLI copilot plugin marketplace add gbourgeat/ddd-ts-kit, puis copilot plugin install ddd-ts-kit@ddd-ts-kit par son nom, start
Agent cloud de Copilot, VS Code déclaré par le .github/copilot/settings.json qu'écrit init par son nom, start

Vingt-deux skills conduisent un projet d'une expression des besoins jusqu'à des contextes qui fonctionnent, quatre agents explorent, construisent un contexte dans son propre worktree, écrivent les specs et relisent, et le hook refuse un fichier écrit à la main là où un générateur existe et vérifie chaque fichier écrit. Même sans plugin, l'AGENTS.md et les règles par couche qu'écrit init guident chaque agent. Le plugin pour agents de code liste les skills et dit ce que chaque hôte exécute.

Package Ce qu'il apporte
ddd-ts-kit la CLI : init, generate, check, docs, sdk, status, rules, explain et d'autres
@ddd-ts-kit/kernel entités, agrégats, objets-valeurs, erreurs, ports, les déclarations, bus de commandes, de requêtes et d'événements, les interfaces standard ; aucune dépendance
@ddd-ts-kit/events événements d'intégration typés, outbox transactionnelle et relais, AsyncAPI et catalogue d'événements
@ddd-ts-kit/rabbitmq, kafka, nats, redis/event-bus fournisseurs de brokers derrière un seul contrat : publication acquittée, abonnements durables, nouvelles tentatives, files de rejet
@ddd-ts-kit/scheduling des jobs lancés par le temps qui ne font que déclencher des cas d'usage, une seule exécution sur l'ensemble des instances
@ddd-ts-kit/auth acteurs, une permission par cas d'usage, refus par défaut ; autoriseurs par rôles, par attributs et par relations ; JWT
@ddd-ts-kit/openfga les relations d'auth sur un serveur OpenFGA
@ddd-ts-kit/aspects journalisation, métriques, nouvelle tentative, délai d'expiration, disjoncteur, cache et cloison appliqués de l'extérieur
@ddd-ts-kit/audit journal d'audit chaîné par hachage, en ajout seul
@ddd-ts-kit/redis cache, verrou, limiteur de débit et signal de relais sur Redis, Dragonfly ou Valkey
@ddd-ts-kit/s3 stockage de fichiers sur AWS S3, MinIO, OVHcloud, Scaleway ou Cloudflare R2
@ddd-ts-kit/http des routes déclarées une fois, montées sur Express, Fastify, Hono ou NestJS ; erreurs RFC 9457 ; OpenAPI 3.1 ; cache HTTP
@ddd-ts-kit/validation un seul port Schema au-dessus de zod, class-validator, TypeBox et Standard Schema
@ddd-ts-kit/di des modules de contexte sur tsyringe, les classes de port comme jetons, une application de test avec des fakes
@ddd-ts-kit/pino, winston le port Logger du kernel sur pino ou winston
@ddd-ts-kit/sentry le port ErrorReporter du kernel sur Sentry ou GlitchTip
@ddd-ts-kit/tracing OpenTelemetry câblé une fois par la plateforme
@ddd-ts-kit/testing builders, scénarios étant donné / quand / alors, suites de contrat, harnais de bout en bout
@ddd-ts-kit/drizzle, knex, prisma, typeorm unité de travail, stockage de l'outbox, modèles de lecture, isolation des tenants, magasin d'événements
@ddd-ts-kit/eslint-plugin les règles de code sur l'arbre syntaxique, avec des préréglages

Tout passe par des ports du kernel, si bien que chaque pièce est remplaçable : un autre broker, un autre ORM ou un autre framework HTTP est un adaptateur qui passe la même suite de contrat. Le README de chaque package dit ses limites et ce qui a été vérifié.

Où chaque package est importé : dans un contexte délimité, le domaine n'importe que le kernel, l'application y ajoute auth, la présentation importe http et validation, l'infrastructure un package d'ORM, le langage publié events et validation ; la plateforme compose les contextes avec di et les packages de ses capacités techniques

init --ci github|gitlab|both installe les mêmes portes sur l'une ou l'autre plateforme : vérification des types, lint, architecture, code mort, tests unitaires avec seuils de couverture, tests d'intégration sur PostgreSQL, tests de bout en bout, dérive de la documentation et du contrat, sécurité et construction de l'image, derrière une seule porte finale. Chaque outil est gratuit sur n'importe quel dépôt. Les détails : pipeline/README.md.

examples/scheduling est une plateforme de prise de rendez-vous construite avec le kit comme un utilisateur la construirait : à partir d'une expression des besoins, à travers les neuf étapes, par plusieurs workers en parallèle. Quatre contextes délimités sur Express, class-validator, tsyringe, Knex sur PostgreSQL, l'outbox transactionnelle, Keycloak et une autorisation par relations.

Node 22.12 ou ultérieur. Les projets générés vérifient leurs types avec le compilateur TypeScript 7 natif et gardent TypeScript 6 pour les outils qui ont encore besoin de son API.

ddd-ts-kit se construit en public, et les contributions de toute taille sont les bienvenues : une coquille, une traduction, un test, un adaptateur ou une nouvelle règle. De bons points de départ :

  • les issues étiquetées good first issue ou help wanted ;
  • un adaptateur pour un broker, un ORM ou un framework HTTP que le kit ne prend pas encore en charge : il passe la suite de contrat de son port ;
  • la documentation, en anglais ou en français, et l'application d'exemple ;
  • un retour d'un vrai projet, dans les Discussions : ce qui a tenu, ce qui a gêné.

CONTRIBUTING.fr.md donne l'installation, les règles de base et le chemin d'un changement jusqu'à main. Si ddd-ts-kit vous est utile, une étoile sur GitHub aide d'autres personnes à le trouver.

Les contributeurs de ddd-ts-kit

Historique des étoiles de ddd-ts-kit

MIT © Gary Bourgeat et les contributeurs.