Comment combiner TDD et DDD strict?

15

TDD consiste à concevoir du code, guidé par des tests.
Ainsi, les couches typiques ne sont généralement pas construites en amont; ils devraient légèrement apparaître lors des étapes de refactorisation.

La conception basée sur le domaine implique de nombreux modèles techniques, définissant des couches bien établies comme la couche Application, la couche Infrastructure, la couche Domaine, la couche Persistance.

Pour démarrer le codage d'un projet DDD à partir de zéro, comment se comporter?
Dois-je strictement laisser le design émerger des tests, ce qui signifie pas de séparation des préoccupations (pas de couches) et de refactorisation afin de s'adapter aux modèles techniques DDD?

Ou devrais-je créer ces couches vides (application, entités / services de domaine, infrastructure) et laisser TDD s'intégrer dans chacune d'elles indépendamment (en utilisant des maquettes pour isoler entre les couches)?

Mik378
la source

Réponses:

12

Assurez-vous de lire les commentaires récents de l'oncle Bob sur le rôle du design dans TDD .

La conception basée sur le domaine implique de nombreux modèles techniques, définissant des couches bien établies comme la couche Application, la couche Infrastructure, la couche Domaine, la couche Persistance.

Udi Dahan: "Dieu, je déteste la superposition." Il passe un peu de temps à en discuter dans son discours CQRS - mais différent (la superposition commence à 18h30)

J'écrirais votre phrase légèrement différemment; "DDD reconnaît qu'il existe un certain nombre de problèmes communs à la plupart des applications d'entreprise et que les solutions à ces problèmes ont des durées de vie différentes" .

Par exemple, les problèmes de domaine, en règle générale, doivent être flexibles - en particulier lorsque vous personnalisez une solution pour une entreprise particulière. Après tout, le domaine concerne la façon dont l'entreprise fait des affaires, c'est-à-dire comment l'entreprise gagne de l'argent et être en mesure d'apporter rapidement des améliorations commerciales est un revenu gratuit.

D'un autre côté, vous n'avez probablement pas besoin de changer souvent le composant de persistance. La solution de base de données qui fonctionnait avec la dernière version fonctionnera probablement également avec cette version.

Les problèmes d'application sont quelque part au milieu; ils ont tendance à être stables afin que les utilisateurs n'aient pas besoin d'apprendre une nouvelle application à chaque version.

En outre, il peut y avoir plusieurs implémentations pour résoudre un problème donné. Par exemple, l'application peut n'avoir besoin que d'un instantané de son état actuel - il suffit d'enregistrer un fichier sur le disque. Et dans vos premières itérations, cela peut aussi être tout ce dont le domaine a besoin. Mais finalement, une histoire appelle la prise en charge de requêtes ad hoc, et vous reconnaissez que la configuration d'une base de données relationnelle sera beaucoup plus facile que de l'implémenter à partir de zéro. Et puis il y a cette fonctionnalité qui fonctionnerait mieux dans une base de données de graphiques.

Pendant ce temps, le CTO veut une version de l'application qui fonctionne sur son téléphone; le PDG vient d'entendre un gars dire que la publication d'une API est la grande chose.

De plus, l'équipe commerciale utilise un modèle différent, alors donnez-nous la même application, avec un modèle différent. Oh, mais nous voyageons beaucoup, donc notre version doit fonctionner lorsque nous sommes hors ligne et se synchroniser plus tard ...

En d'autres termes, vous appliquez les modèles tactiques de non pas en implémentant des espaces réservés vides et en supposant qu'ils seront remplis plus tard, mais plutôt en reconnaissant lorsque vous traversez les flux "Hé, c'est du code de persistance dans mon modèle de domaine, je ne dois pas être refactoring terminé. "

VoiceOfUnreason
la source
11

Le développement piloté par les tests (TDD) n'est pas une conception. C'est une exigence qui a un impact sur votre conception. Tout comme si vous deviez être thread-safe, ce n'est pas une conception. Encore une fois, c'est une exigence qui a un impact sur votre conception.

Si vous ignorez joyeusement toutes les autres préoccupations de conception et respectez religieusement les règles TDD, ne blâmez pas TDD lorsque votre code se transforme en merde. Ce sera de la merde testable mais ce sera de la merde.

Une bonne chose à propos de la merde testable est que c'est de la merde refactorisable donc pour certaines personnes, c'est assez bon. Nous n'aurons de fantaisie qu'en cas de besoin. D'autres détestent cela et blâment TDD pour cela. Non, c'est ta faute.

La conception pilotée par domaine (DDD) est quelque chose que vous faites avant le cycle de refactorisation rouge vert de TDD.

DDD est l'effort de créer et de préserver un espace dans le code où un expert du domaine, qui est largement inconscient des détails du système, peut comprendre comment contrôler le système. Cela se fait par abstraction et modélisation d'un domaine problématique de manière familière.

Un système DDD peut avoir une architecture qui ressemble à ceci:

entrez la description de l'image ici

Cette architecture DDD porte de nombreux noms: Clean , Onion , Hexagonal , etc.

Voici la déconnexion que je vois beaucoup de gens quand ils regardent cette conception. Ce n'est pas concret. Je peux suivre cette conception et ne jamais avoir écrit quelque chose que vous voyez ici. Je vois que d'autres insistent sur le fait qu'il doit y avoir un objet de cas d'utilisation ou une classe d'entités. Il s'agit d'un ensemble de règles qui vous indiquent à qui vous pouvez parler et comment.

C'est ça. Suivez les règles de cette conception et vous pouvez TDD votre petit cœur. TDD ne se soucie pas de qui vous parlez. Il se soucie que tout ce qui fait quelque chose puisse être prouvé pour fonctionner ou non au clic d'un bouton. Non, quelque chose quelque part est cassé. Il vous dit exactement ce qui est cassé.

Toujours trop vague? Regardez le diagramme Controler- Use Case Interactor- Presenterdans le coin inférieur droit. Voici trois choses concrètes qui communiquent entre elles. Bien sûr, c'est DDD, mais comment ajoutez-vous TDD ici? Il suffit de se moquer des trucs en béton. Le présentateur doit recevoir des informations. Un PresenterMockcours serait un bon moyen de vérifier qu'il obtient ce que vous attendez. Remettez le Use Case Interactorla PresenterMocket conduire le Use Case Interactorcomme si vous étiez Controlleret vous avez un bon moyen de tester l' unité la Use Case Interactordepuis la maquette vous dira si elle a obtenu ce que vous attendiez à obtenir.

Regardez ça. TDD satisfait et nous n'avons pas eu à faire de futz avec notre conception DDD. Comment est-ce arrivé? Nous avons commencé avec un design bien découplé.

Si vous utilisez TDD pour piloter la conception (pas simplement le développement ), vous obtenez une conception qui reflète l'effort que vous y mettez. Si c'est ce que vous voulez bien. Mais ce n'était jamais ce à quoi TDD était destiné. Ce qui finit par manquer n'est certainement pas la faute de TDD.

TDD n'est pas une question de design. Si vous devez apporter des modifications à la conception pour utiliser TDD, vous rencontrez des problèmes plus importants que les tests.

candied_orange
la source
Je n'ai jamais dit que TDD est un design, mais il s'agit de design.
Mik378
1
Oncle Bob vous disait de concevoir. Il ne vous disait pas "hé si vous testez le travail qui se soucie du reste".
candied_orange
1
Comme je l'ai déjà dit, suivez simplement les règles avec qui vous êtes autorisé à parler. L'architecture propre n'est pas un sujet de débat BDUF. Identifiez simplement la partie dans laquelle vous vous trouvez et pensez à qui et comment communiquer. C'est plus agile que vous ne le pensez. Après cela, demandez s'il est testable par TDD. Si ce n'est pas le cas, vous avez fait quelque chose de mal. Si c'est le cas, j'espère que vous y prêtiez attention car un bon design est plus que simplement testable.
candied_orange
6
Ugh ... Je ne supporte vraiment pas le terme inapproprié "Test Driven Design". Vous devez encore concevoir un peu avant de commencer à marteler béatement votre clavier, que vous écriviez ou non des tests.
RubberDuck
1
Pour citer l'oncle Bob sur ce point précis, "Vous devez concevoir la période" . Cliquez ici et si vous êtes trop impatient de lire le tout, recherchez ces mots. Vous constaterez que M. Martin est catégorique: le TDD n'est pas une solution miracle et vous devez concevoir non seulement votre code mais aussi vos tests si vous ne voulez pas vivre dans une base de code très fragile.
candied_orange
4

TDD assure que votre code a tous les cas de test nécessaires écrits en parallèle au développement. Cela ne devrait pas affecter la conception de haut niveau. Pensez-y davantage dans le travail des tranchées.

DDD concerne les conceptions de haut niveau, le langage entre les experts et les ingénieurs du domaine, la cartographie du contexte, etc. Cela devrait être le moteur de la conception de haut niveau de l'application.

Ce sont deux explications superficielles de deux méthodologies de programmation puissantes. Mais à la fin de la journée, ils accomplissent vraiment deux choses très différentes.

Commencez par le langage DDD et le mappage de contexte, puis éventuellement lorsque vous allez écrire le code, commencez la pratique de TDD. Mais la pratique du TDD ne devrait pas affecter la conception de haut niveau, mais elle devrait garantir que les choses peuvent être testées. Il y a une petite mise en garde ici.

Je pense qu'il peut être important de noter: vous ne devez pratiquer DDD que si l'application est suffisamment complexe.

Matt Oaxaca
la source
1
Je ne suis pas d'accord, TDD n'est pas une question de test, c'est une question de conception.
Mik378
Je fonde tout sur les 3 règles du TDD telles que décrites par l'oncle Bob.
Matt Oaxaca
Steve Freeman, auteur du livre GOOS, a déclaré: vous ne devez spécifier aucune couche ou infrastructure avant de démarrer des cycles TDD.
Mik378
Je ne connais pas ce livre, mais je dois être en désaccord. Je ne veux pas que TDD façonne mon graphique DI et de classe.
Matt Oaxaca
3
@ Mik378: TDD ne concerne pas les tests, mais ne concerne pas non plus principalement la conception - la seule conception induite par TDD est la conception pour la testabilité unitaire. Toutes les autres parties de la conception doivent provenir d'ailleurs. Je vois TDD plus comme une technique de mise en œuvre.
Doc Brown
3

DDD concerne la conception de logiciels.
TDD concerne la conception de code.

En DDD, le "modèle" représente la désabstraction du domaine, toutes les connaissances de l'expert du domaine.

Nous pourrions utiliser TDD pour le modèle de conception de logiciel initial de code. Le domaine a des règles métier et des modèles de domaine que le test écrit (en premier) doit être vert.

En effet, nous pouvons coder les tests, après avoir conçu un modèle piloté par domaine.
Ce livre "Growing Object-Oriented Software, Guided by Tests" link-for-buy
Adoptez cette approche, avec un squelette ambulant , une architecture hexagonale et TDD.

Source: DDD rapidement - InfoQ

JonyLoscal
la source
1
bien pour moi, le logiciel et le code, c'est la même chose
Konrad
1
Ça pourrait être pareil aussi. J'ai essayé de dire: logiciel comme "solution", "système", "haut niveau" et code comme "implémentation", "bas niveau", "détails".
JonyLoscal
Je pense que l'important est que "d'abord nous testons, mais nous avons besoin d'un squelette minimal où nous commencerions à tester". Le faites vous?
JonyLoscal
1

Dois-je strictement laisser le design émerger des tests

Non. (Piloté par domaine) La conception par définition doit émerger des exigences du domaine. C'est une mauvaise idée de laisser quoi que ce soit d'autre pour piloter votre conception, que ce soit la suite de tests, le schéma de base de données ou ... (à suivre)

Ou devrais-je créer ces couches vides (application, entités / services de domaine, infrastructure) et laisser TDD s'intégrer dans chacune d'elles indépendamment

(suite) ... ou quelques couches canoniques de classes / hiérarchies de classes dans le langage OO de votre choix, même s'il est très mature et populaire (après tout, "des millions de mouches ne peuvent pas se tromper", non?) .

En ce qui concerne DDD, la POO échoue à exprimer des exigences sous une forme lisible par l'homme, c'est-à-dire quelque chose qui serait plus ou moins clair pour un non-programmeur. Les langages FP strictement typés font un meilleur travail. Je recommande de lire un livre sur DDD en utilisant la programmation fonctionnelle "Domain Modeling Made Functional" par Scott Wlaschin

https://pragprog.com/book/swdddf/domain-modeling-made-functional

Vous n'avez pas besoin d'utiliser le langage FP pour emprunter certaines des idées à partir de là (pas toutes malheureusement), mais si vous le lisez, vous voudrez probablement utiliser un langage fonctionnel.

Il répondra également à votre question sur la façon dont TDD s'intègre dans l'image DDD. En un mot, lorsque les exigences sont codées dans un style fonctionnel, cela élimine le besoin d'une grande quantité de tests unitaires car il rend la plupart des états et scénarios invalides irreprésentables / impossibles à compiler. Bien sûr, il y a encore de la place pour les tests automatisés dans le projet FP mais en aucun cas les tests ne guideront les décisions de conception majeures.

Pour faire un cercle complet, revenons à la question du titre, c'est-à-dire "Comment combiner TDD et DDD strict?". La réponse est simple: il n'y a rien à combiner / pas de conflit d'intérêts. Concevoir selon les exigences, développer selon la conception (en écrivant d'abord des tests si vous voulez vraiment faire du TDD)

KolA
la source
+1 pour le livre FP DDD et l'explication générale.
Roman Susi