Domain-Driven Design en Go : Comment ne pas se noyer dans les couches d'abstraction

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

Introduction : Pourquoi votre projet Go réclame-t-il du DDD ?

\n\n

Avez-vous déjà écrit un service en Go qui commençait avec quelques endpoints et trois tables en base de données, et qui, six mois plus tard, se transformait en monstre avec des fichiers handler.go de plusieurs kilomètres et des nombres magiques éparpillés dans tout le code ? Cela vous dit quelque chose ? Moi aussi, je suis passé par là.

\n\n

Le problème du développement classique en Go est que nous pensons trop tôt aux bases de données et au JSON, en oubliant l'essentiel : la logique métier. Le Domain-Driven Design (DDD) vient à la rescousse justement quand on souhaite mettre de l'ordre dans le chaos. Mais comment appliquer le DDD en Go, alors que le langage ne nous gâte pas avec des classes et de l'héritage ? Voyons cela ensemble.

\n\n

Dans cet article, je vais vous montrer comment appliquer le Domain-Driven Design dans des projets Go sans fanatisme excessif, avec des exemples de code fonctionnels et une structure claire. Vous apprendrez à séparer le bon grain de l'ivraie et à écrire un code compréhensible non seulement par la machine, mais aussi par l'analyste métier.

\n\n

Qu'est-ce que le DDD et pourquoi est-il nécessaire pour un développeur Go ?

\n\n

Le Domain-Driven Design ne concerne pas la technologie. Il concerne le langage. Il s'agit de faire en sorte que le code parle le langage du métier. Si votre domaine est un site de e-commerce, le code doit contenir Order, Product, Cart, et non DataTransferObject ou EntityManager.

\n\n

Go, avec sa simplicité et son absence de magie, est parfaitement adapté au DDD. Pas de classes proxy ni de réflexion qui cachent l'essentiel. En Go, vous êtes obligé d'être honnête : si vous avez un agrégat, c'est une structure explicite ; si vous avez un repository, c'est une interface.

\n\n

Les principaux blocs de construction du DDD que nous allons examiner :

\n
    \n
  • Entity (Entité) — un objet avec un identifiant unique (par exemple, User).
  • \n
  • Value Object (Objet-valeur) — un objet immuable décrivant des caractéristiques (par exemple, Address).
  • \n
  • Aggregate (Agrégat) — un cluster d'entités garantissant la cohérence (par exemple, Order avec OrderItem).
  • \n
  • Repository (Repository) — une collection d'objets cachant les détails de stockage.
  • \n
  • Domain Service (Service domaine) — une opération qui ne s'intègre pas dans une seule entité.
  • \n
\n\n

Maintenant, traduisons cela en Go.

\n\n

Structure du projet : comment ne pas se perdre dans les dossiers

\n\n

Bonne nouvelle : Go n'impose aucune structure. Mauvaise nouvelle : sans structure, vous vous noierez rapidement. Pour le DDD en Go, je recommande l'organisation de code suivante :

\n\n
votre-projet/\n├── cmd/\n│   └── server/\n│       └── main.go\n├── internal/\n│   ├── domain/          # Cœur pur : entités, règles\n│   │   ├── order.go\n│   │   ├── product.go\n│   │   └── errors.go\n│   ├── application/     # Cas d'utilisation / services applicatifs\n│   │   ├── order_service.go\n│   │   └── product_service.go\n│   └── infrastructure/  # Bases de données, API externes, handlers HTTP\n│       ├── postgres/\n│       │   └── order_repository.go\n│       └── http/\n│           └── handler.go\n└── go.mod
\n\n

Notez que domain n'importe pas infrastructure. C'est la règle d'or du DDD : le domaine ne sait rien des bases de données ni de HTTP. Il vit dans un vide de structures et d'interfaces pures.

\n\n

Écrire le domaine : Order et Value Objects

\n\n

Commençons par le cœur. Supposons que nous construisons un système de gestion de commandes. Voici à quoi pourrait ressembler l'entité Order :

\n\n
// internal/domain/order.go\npackage domain\n\nimport (\n    "errors"\n    "time"\n)\n\ntype OrderStatus string\n\nconst (\n    OrderStatusPending   OrderStatus = "pending"\n    OrderStatusConfirmed OrderStatus = "confirmed"\n    OrderStatusCancelled OrderStatus = "cancelled"\n)\n\ntype OrderID string\n\ntype Order struct {\n    ID         OrderID\n    CustomerID string\n    Items      []OrderItem\n    Status     OrderStatus\n    CreatedAt  time.Time\n    Upd                

Blogs

Book Recommendations