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?
.
Ou dois-je créer ces couches vides (application, services d'entités/de domaine, infrastructure) et laisser TDD s'intégrer dans chacune d'elles indépendamment (en utilisant des maquettes pour isoler entre les couches)?
Assurez-vous de passer en revue les commentaires récents de l'oncle Bob sur le rôle de conception 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 du 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 ddd non pas en implémentant des espaces réservés vides et en supposant qu'ils seront remplis plus tard, mais en reconnaissant plutôt quand vous traversez les flux "Hé, c'est du code de persistance dans mon modèle de domaine, je ne dois pas encore refactoriser."
Test Driven Development (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.
Domain Driven Design (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:
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 se fiche 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 Controler
- Use Case Interactor
- Presenter
diagramme dans 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 du béton. Le présentateur doit recevoir des informations. Une classe PresenterMock
serait un bon moyen de vérifier qu'elle obtient ce que vous attendiez. Remettez le Use Case Interactor
le PresenterMock
et pilotez le Use Case Interactor
comme si vous étiez le Controller
et vous avez une belle façon de tester à l'unité le Use Case Interactor
puisque la maquette vous dira si elle a obtenu ce que vous attendiez.
Regardez ça. TDD était satisfait et nous n'avions pas besoin 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 [~ # ~] d [~ # ~] développement) vous obtenez une conception qui reflète l'effort que vous y mettre. 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.
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, le mappage de 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 pourrait être important de noter: vous ne devriez pratiquer DDD que si l'application est suffisamment complexe.
[~ # ~] ddd [~ # ~] concerne la conception de logiciels.
[~ # ~] tdd [~ # ~] concerne la conception du 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 "Développement d'un logiciel orienté objet, guidé par des tests"link-for-buy
Adoptez cette approche, avec un squelette ambulant , architecture hexagonale et TDD.
Source de: DDD rapidement - InfoQ
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 dois-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 la langue de votre choix OO préférée, même si elle est très mature et populaire (après tout, "des millions de mouches ne peuvent pas se tromper" , droite?).
En ce qui concerne DDD, OOP é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 langues FP strictement saisies 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 effectivement le lire, vous voudrez probablement utiliser un langage fonctionnel.
Il répondra également à votre question sur la manière 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)