2017-06-07
Mes notes brutes sur l'après-midi du DDD :)
Merci à @tpierrain, @brunoboucard, @jgrodziski, Microsoft, 42skillz
-
Il y a 30 ans, Plan "Informatique pour tous"
- TO7 très abordable, très interactif
-
Aujourd'hui, encore plus simple (plein de ressources pour apprendre en ligne, coder dans le browser)
-
Mais pourtant : "The cost of maintaining software stays too high"
-
C'est quoi du legacy ?
- Déf. de Michael Feathers : un code qui n'a pas de test
- Thomas Pierrain : "un code qui fait souffrir les gens (utilisateurs & devs)"
3 types de complexité :
- Complexité logicielle
- Complexité du domaine
- Complexité des interactions humaines
DDD = Combattre la complexité au cœur du logiciel
- Idée : visualiser la complexité logicielle
- les dépendances par ex ?
Exemples de domaine :
- Comptabilité à double entrée.
- Fabriquer un IDE
DDD : résoud des problèmes, définit des contextes.
- Aligner (connecter) l'espace du problème et l'espace de la solution.
- Comment alligner ? 1ère étape = la compréhension.
"Comment intégrer au mieux le domaine dans le logiciel"
"Build what they need, not what you can"
- Business value
- Language is key
- Make the implicit explicit. Pourquoi ? Pour se comprendre.
Ce n'est pas un process.
Design is making decisions
Always connect your decisions to your business objective
On prend des décisions pendant qu'on code, les développeurs font du design.
S'assurer que les décisions sont conscientes.
Gérer au mieux : pourquoi est-ce que je prends cette décision ? Est-ce que c'est adapté ?
Principes (souple design) et patterns stratégiques pour nous aider quand on code.
Coder un algo d'attribution de places de train
Le client : "Le coût d'évolution a augmenté".
Base : kata d'Emily Bache de réservation de billets
Mais "légacifié très fort".
Séance d'exploration pour prendre connaissance du code :
- On peut faire de petits changements auto par l'IDE
- Pas trop de changement car on n'a pas de tests
- On ne touche pas à l'API fournie
- Fil d'ariane, noter l'avancement de l'exploration
Commencer par un test simple pour se donner du courage et commencer à couvrir le code.
Simulacre = stub
Le premier test est douloureux à écrire, car on se prend toutes les dépendances
Dans le test d'acceptance des mocks sont utilisés. Les interfaces permettront plus tard d'utiliser ces mêmes tests d'acceptance avec la vraie base, comme tests d'intégration (e2e).
Astuce : préparer des snippets ad hoc pour préparer un kata (par exemple du json pour les tests d'acceptance)
-
Objets adolescents (M. Feathers) : qui n'ont pas encore pris leurs responsabilités (ex: classe avec que des attributs)
-
Un bon test : si l'expert du domaine peut lire le code (ou un dev peut lui lire texto) et qu'il comprend.
-
Pour éviter d'avoir une charge cognitive on fait le choix d'être conforme avec les mots du backend extérieur à l'application. Mais on est libre sur les concepts qui nous sont propres.
-
Idée de convention : dans les descriptions de tests, mettre des majuscules aux concepts métier
-
Entity : qui a une identité (pour nous) & mutable (pouvoir suivre ses évolutions dans le temps). Associé à une date, on peut faire des snapshots. On peut aussi l'interpréter comme une succession de valeurs avec un identifiant qui ne bouge pas.
-
Value Type : immutable, pas d'identité (pour nous), égalité par propriété.
- Peut avoir du comportement
-
Closure of operations : au lieu de modifier l'état interne d'un objet, une opération sur un objet retourne un objet du même type. Pour éviter d'introduire des dépendances.
- Exemple : Coach.addSeat(seat) ne modifie pas le Coach, mais en renvoie un nouveau.
-
"Quand il y a un agrégat il y a un invariant."
-
Le 'I' des noms d'interfaces : intéressant s'il veut dire "Je". Exemple :
interface IReserveSeats
.
-
Code tech mélangé au code domaine : ça revient vite, comme des mauvaises herbes
-
Hexagonal Architecture = Testable Architecture : par opposition à l'architecture en couches, dans laquelle il est plus compliqué de tester la couche métier
-
Le code du domaine change moins souvent que le code infra, ou n'a pas le même cycle de vie
-
Port : l'intention, l'API. Les interfaces à l'intérieur du domaine.
-
Adaptateur = la somme des choses qu'il faut faire pour que ça marche (sérialisation / déserialisation). Toute la normalisation est dans l'adaptateur, le domaine reste propre.
-
Archi hexa : ça marche pour la maintenabilité
-
3 étapes au démarrage de l'appli :
- Instancier les adaptateurs pour sortir (SPI, service providers, consommés par l'application, ex : bdd, services HTTP extérieurs)
- Instancier l'hexagone
- Instancier les adaptateurs pour entrer (API, fournie par l'application, ex : API HTTP fournie par l'application)
-
Astuce : concrétiser l'hexagone en ayant une classe Hexagon. Un wrapper léger sur l'API.
- Ensuite, appels à l'API à travers des adaptateurs pour entrer qui prennent l'hexagone en constructeur
- L'instanciation faite, on ne garde de références que vers les adaptateurs, on n'a plus besoin explicitement de la référence vers l'hexagone.
// Initialisation...
var hexagon = new Hexagon(trainDataService, ...)
var reserveSeatsAdapter = new ReserveSeatsRestAdapter(hexagon);
// Ailleurs...
reserveSeatAdapter.Post(new ReservationRequestDto(){number_of_seats = 3, train_id = "1234"});
Méthode Post
de ReserveSeatsRestAdapter
public Task<string> Post(ReservationRequestDto reservationRequestDto) {
var train_id = reservationRequestDto.train_id;
var number_of_seats = reservationRequestDto.number_of_seats;
var jsonResult = hexagon.Reserve(reservationRequestDto.train_id, reservationRequestDto.number_of_seats);
// ...
}
Les utilisateurs (ou le code applicatif) vont appeler explicitement uniquement les adaptateurs "pour entrer" (API). Les SPI (service providers) seront appelés indirectement par l'hexagone à travers les interfaces métier.
"Focus on, highlight and protect the business value in your code!"
-
Note : dans une archi CQRS, les méthodes de l'hexagone ne renvoient rien (on passe par des pub sub, des queues par exemple)
-
L'adaptateur est un bon endroit pour mettre un anti-corruption layer.
-
Exemple douloureux d'équipe où le lead dev dit "les experts métier sont des blaireaux, on a nos définitions à nous" et ou le code est différent de ce que dit le métier
- des bugs
- incompréhensions
- Lire du code toute la journée qui ne dit pas la même chose que ce qu'on entend en réunion = fatiguant