-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation
Description
Déroulé du talk
0. Prérequis
Liens de référence:
Étapes de préparation pré-live-coding:
-
Mettre VSCode en thème "fond clair".
-
Installer Snippet - Visual Studio Marketplace pour VSCode.
-
Créer snippet "
basic functional test" avec Snippet - Visual Studio Marketplace:contenu du snippet
// @ts-check // run with: $ npx mocha test/functional/createPlaylist.functional.tests.js const assert = require('assert'); const { createPlaylist } = require('../../app/domain/features'); describe('playlist', () => { it('should be created by a user without playlist', async () => { const playlistName = 'summer mega mix 2022'; const playlist = await createPlaylist('userWithoutPlaylist', playlistName); assert.equal(playlist.id, 0); assert.equal(playlist.name, playlistName); }); it('should be created by a user with playlist', async () => { const playlistName = 'summer mega mix 2023'; const playlist = await createPlaylist('userWithPlaylist', playlistName); assert.equal(playlist.id, 1); assert.equal(playlist.name, playlistName); }); });
-
Préparer l'environnement de live coding, dans le terminal de VSCode:
nvm use npm install git checkout migration-start # ancienne version: migration-start-devoxx-2022 docker compose up -d mongo . ./env-vars-testing.sh export MONGODB_PORT=27117 npm run test-reset
1. Faire une démo d'OpenWhyd
Adrien
- La démo
- Décrire le problème : Les contributeurs ont lâché l'affaire car trop fragile et trop difficile à comprendre
- Survol de la code base :
- Partir de Application.js
- préciser que: pas de typage
- parler du framework maison => fichier
app.route, notamment lesapi-->subdir-->controllers/api/post.js--> fonction intéressante: insertion d'un morceau de musique. (post Controller sur la méthodeinsert)
Objectifs:
- Permettre à quelqu'un qui n'a pas d'expérience sur la code base d'openWhyd d'être autonome dans la correction de bugs.
- Le but étant de maintenir l'existant, on veut améliorer sur place ! On ne veut pas faire une fuite en avant, en créant un module from scratch qui paraitrait plus simple à faire mais qui rajouterait de la complexité de maintenance sur le legacy
Donc => Migration in situ
2. Préparation du terrain opératoire: Biopsies Clean Codiennes
But : Y voir plus claire sur la code base
But caché (intention) : Montrer la fragilité de la code base
- Expliquer que l'on vise de migrer l'action
insertdecontrollers/api/post - Exécuter les tests d'intégration pour montrer qu'on en a :
npm run test:integration:post # avant, on lançait le serveur, puis: $ npm run test:integration- Changer dans
controllers/api/post,ppuisuIduNmde l'objetqdansinsert. Et expliquer qu'on est en train de changer la structure en base de données. (redémarrer serveur puis)Réexécuter les tests d'intégration et montrer que les messages d'erreurs ne permettent pas de voir ce qu'on a cassé
3. Ajout des approval tests
git checkout migration-start-with-approval-tests
npm install- Expliquer le principe des Approval tests, théorie + pratique.
- Montrer un fichier d'approval de
When posting a track - Ouvrir
approval.tests.jset expliquer le test correspondant - Lancer le test :
npm run test:approval- Montrer dans les logs que l'on a un diff des approvals.
- Supprimer la modification de userName et userId
4. Amener plus de lisibilité dans le code
Lisibilité
Adrien:
- Survol de
insertjusqu'àprocess playlisten expliquant le code - Montrer que
inserta beaucoup de responsabilité => décider que la partiecreatePlaylistest déjà un scope suffisant à refactorer
Jordan:
Extraction deextractPlaylistRequest- Passage du bloc try-catch en pure fonction
postRequest.pl = extractPlaylistRequest(...)et dire que ça pourrait être fait directement à l'initialisation depostQuery.- Remplacer l'initialisation à
undefinedparextractPlaylistRequest(...) - Extraction
needToCreatePlaylistdans le if - Comment rendre la séquentialité de
createPlaylistplus explicite ?
Adrien:
- Wrapper dans une Promise
createPlaylist() - Virer le
actualInsert()superflu
Diagnostic
- Julien : Mais il fait quoi ce
createPlaylist? - Adrien : Présente
userModel.createPlaylist() - Jordan & Julien s'étonnent et commentent du couplage du métier dans la couche de persistence
5. Modéliser le domaine
- Créer le répertoire
domain - Créer le contrat d'entrée du domaine via l'
api. Le domaine doit dépendre que des objets du domaine, mais il n'y pas de typage fort en JS.
- Julien : On peut migrer TypeScript ?
- Adrien : Trop compliqué mais y'a un autre moyen... Tu veux quoi comme types ?
- Création du type
CreatePlaylistdansapi.ts - Création du type
Playlistdanstypes.ts
- Julien : Mais du coup c'est du TypeScript, comment on va raccrocher les wagon sans migrer de langage ?
- Adrien : Avec la JSDoc
- Refactorer l'appel de la
Promise(createPlaylist)pour bien détourer la déclaration de la fonctioncreatePlaylist
const createPlaylist = (userId, playlistName) =>
new Promise((resolve) =>
userModel.createPlaylist(userId, playlistName, resolve)
);- Appliquer le type
CreatePlaylistvia JSDoc puis activer la vérification avects-check - Montrer que l'on bénéficie du typage dans vscode via l'autocomplétion sur
playlistet les warnings de "compilation" si on fait unplaylist.toto - Extract de
createPlaylistdansfeatures.jsdans le domaine. - Appliquer le
ts-checkdansfeatures - Lancer les approval tests
6. Extraire la logique métier de la couche de persistence
- Remontrer le code de
userModel.createPlaylist()pour caractériser le métier qu'on veut migrer. ⚠️ Créer un test fonctionnel (ex:test/functional/playlist.functional.test.js) pour figer les comportements que l'on a compris et expliquer qu'on ne s'appuie pas sur les Approvals, car le domaine est testé en isolation pure.- Appliquer le snippet
basic functional test(clic droit + "insert snippet") et expliquer les tests - Supprimer la dépendance
userModeldansfeatures.createPlaylist()en la commentant - Résumer le code de
userModel.createPlaylist()et écrire un "algo" dansfeatures
await fetch()
playlist = {
id:
name:
}
await save()
return playlist- Créer
spi.ts - Expliquer que les playlists sont stockées dans les utilisateurs, ce qui nécessite de créer un type
UserRepository. - Ajouter une fonction
getUserByIdqui retourne une Promise de User - Définir User dans
types.ts features a maintenant besoin d'une instance de UserRepository. Comme on ne veut pas se coupler avec le repository, on crée une factorycreateFeatures` pour d'injecter une instance.- Typer le paramètre
userRepositoryde la factory - Appeler
userRepository.getUserbyIddanscreatePlaylist - Retourner dans
userModelet expliquer la logique de création d'un id de playlist, la copier-coller dans features et la nettoyer. - Julien à Adrien : est-ce qu'on doit sauvegarder tout le user pour sauvegarder une playlist ?
- Définir une fonction
insertPlaylistdans la SPI dans leUserRepository - L'appeler dans
features - Replugguer dans le test fonctionnel via
createFeatures - Expliquer la nécessité de créer un Stub de UserRepository
- Créer le stub
userRepositoryet la typer avec la SPI. Générer le squelette au moyen de l'IDE. - Créer une base d'utilisateurs dans le test pour initier le repository en reprenant les ids de user dans les tests fonctionnels
- Implémenter les fonctions du Stubs
- Enrichir les tests pour vérifier la persistance de la playlist
//premier test
assert.deepEqual(users[0].playlists, [playlist]);
//deuxieme test
assert.deepEqual(users[1].playlists[1], playlist);- Faire passer les tests (
$ npx mocha test/functional/playlist.functional.test.js)
7. Recabler le domaine avec la persistance
- Créer un répertoire
infrastructureavecUserCollection.js - Typer
UserCollectionavec la SPI et générer le squelette - Retourner dans le controller et rebinder le domain avec une instance de
UserCollection - Exécution des tests approvals pour vérifier que le câblage fonctionne
- Implémenter
UserCollection.getUserById() - Lancer les approvals montrer que ça casse à cause d'un problème de length sur les playlists. Expliquer la différence de modélisation entre la base et le domaine (playlists vs pl).
- Définir
UserDocumentdansinfrastructure/type.ts, typer le retour de la requête mongo dansgetUserbyIdet tenter de retourner leuserDocument. - Créer la fonction de mapping
mapToUseret expliquer le rôle de l'anti-corruption layer - Relancer les tests d'approval pour montrer que le problème n'est plus là
- Implémenter
insertPlaylist - Montrer que les tests d'approval ne fonctionnent toujours pas à cause de l'inconsistance des données en base (prefs, mid...).
Jordan reprend la main du copilote.
- Jordan : Où est-ce qu'on fait ça ?
- Adrien montre d'où ça vient via le
fetchet lesavedansuserModel - Montrer que la migration de données est faite de manière implicite et n'est pas très robuste (s'il n'y a jamais de save après un save).
- Appliquer la migration de données
git cherry-pick migrate-db # --> commit: 96d94f5ee3e5d4488615ca054a45b34c8e742a9aSi le cherry-pick ne passe pas, vider l'index de git
14. Relancer les approvals
8. Découplage du controller
- Commencer par le controller
posten expliquant qu'on sort du fichier les instanciations. Commenter les require defeaturesetuserCollection - Faire passer
featuresà la stack d'appel deinsertjusqu'àApplication.js - Ne pas oublier
subdir.jsen montrantapp.route - Déplacer la création des objets dans
attachLegacyRoutesFromFile - Relancer les tests d'approval et montrer que ça fonctionne
9. Décommisionnement des Approval tests (optionnel)
git checkout mainAller dans les tests d'intégration de post et de l'infra mongo
TODO:
- Voir comment définir les types en JSDoc de manière globale pour éviter les imports
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation