L'architecture Angular parfaite ?
âArchitectureâ ici fait rĂ©fĂ©rence Ă la structure de fichiers, Ă la façon dont vous la dĂ©coupez en diffĂ©rents composants, services, modules etc et de lâendroit oĂč vous les placez. Aussi, ce qui va suivre est basĂ© sur mon expĂ©rience personnelle, et doit ĂȘtre considĂ©rĂ© comme une boĂźte Ă outils qui, comme toute approche, prĂ©sente des avantages et des inconvĂ©nients.
Chaque fois que je crée ou modifie une architecture Angular, je me pose 3 questions :
- Est-ce que les devsâ trouveront mon architecture agrĂ©able Ă utiliser au quotidien ?
- Est-ce quâelle pourra encaisser les Ă©volutions Ă venir ?
- Est-ce que mon architecture reflÚte mon besoin métier au mieux ?
Chacun de mes choix est confronté à ces 3 questions.
Si les rĂ©ponses sont âouiâ, alors je sais que mon architecture est parfaite, ou plutĂŽt quâelle est parfaitement adaptĂ©e Ă mon contexte mĂ©tier actuel.
Car oui, la rĂ©ponse Ă ces 3 questions dĂ©pendent du logiciel que lâon doit crĂ©er et de lâĂ©quipe en charge. Ce que je veux dire par lĂ câest quâune certaine architecture pourrait trĂšs bien fonctionner dans une situation mais pas une autre car le contexte mĂ©tier (quâon appelle la complexitĂ© inhĂ©rente) est diffĂ©rent. Ainsi, deux architectures dans deux contextes diffĂ©rents peuvent varier drastiquement et il nâexiste pas dâarchitecture qui couvre 100% des cas.
Cependant, les 3 questions que jâai Ă©noncĂ© plus haut ainsi que des principes Ă©prouvĂ©es vont nous permettre dâatteindre notre objectif : concevoir lâarchitecture Angular parfaite pour votre besoin.
Dans cette article, nous allons tenter de comprendre pourquoi ces questions sont importantes et surtout comment y répondre !
Est-ce que mon architecture est agréable ?
LâagrĂ©abilitĂ© ressentie par les devs est primordiale dans vos architectures et doit ĂȘtre au cĆur de chacune de vos dĂ©cisions. Cela fait partie dâun principe quâon appelle la Developer eXperience (DX). Câest Ă dire lâaisance quâont les devs Ă faire leur mĂ©tier.
Pour moi, la DX est la top prio. Nous vivons dans un milieu oĂč les technologies et les besoins mĂ©tiers Ă©voluent constamment, lâimpact de ça câest que vous allez rĂ©guliĂšrement mettre votre code Ă jour car lâAPI dâune librairie ou la version de votre framework ont changĂ©. Egalement, dâun point de vue business, vos PO/PM vous demanderont dâajouter des fonctionnalitĂ©s ou dâen modifier trĂšs rĂ©guliĂšrement. Bref, on passe nos journĂ©es Ă modifier notre code et notre architecture de maniĂšre micro ou macro.
Et changer les choses dans une codebase câest le dĂ©but des embrouilles :
- introduction de bugs dus Ă des effets de bords
- lassitude, agacement ou inquiétudes des devs qui doivent replonger dans un code obscur
Une mauvaise architecture entraĂźne une mauvaise DX qui entraĂźne une Ă©quipe anxieuse et peu efficace. On aura peur de modifier les choses par incomprĂ©hension de la codebase et/ou on aura la flemme de âfaire propreâ car les patterns sont lourds : câest le dĂ©but de la dette technique.
Ainsi, lâimportance dâavoir une architecture agrĂ©able Ă utiliser est primordiale pour la santĂ© mentale des devs et donc la pĂ©rennitĂ© du business.
Si lâĂ©quipe de dĂ©veloppement trouve la structure de fichiers cohĂ©rentes et facile Ă naviguer alors elle pourra ajouter, modifier ou supprimer des Ă©lĂ©ments plus rapidement et sans crainte dâeffet de bord.
Ce qui participe Ă©galement Ă lâagrĂ©abilitĂ© de navigation dans une architecture câest de garder le nombre de fichiers et dossiers bas tout en respectant la separation of concerns. La multiplication de fichiers peut tout Ă fait rĂ©pondre Ă un besoin, mais elle peut Ă©galement ĂȘtre vecteur dâerreurs. On doit trouver des noms adĂ©quats, on doit faire attention Ă ne pas mĂ©langer les responsabilitĂ©s des fichiers, et au-delĂ de ça cela apporte de la lourdeur Ă la navigation entre fichiers.
AprĂšs tout, si Angular et NgRx sâĂ©vertuent Ă rĂ©duire le boilerplate de leurs produits, câest parce quâils ont bien compris lâimportance dâavoir un workspace lĂ©ger.
Ne vous mĂ©prenez pas sur ce que je dis, je ne vous incite pas Ă avoir un seul gros composant, service ou store avec tout dedans. Je vous incite Ă dĂ©couper les choses quand il y a rĂ©ellement une raison de le faire. Lorsque vous vous apprĂȘtez Ă dĂ©couper un fichier en plusieurs , posez vous la question du gain et utilisez votre bon sens. Gardez la separation of concerns comme indicateur.
Ne sous-estimez pas lâimportance de la DX. Câest le nerf de la guerre. Posez vous la question de savoir si une nouvelle personne qui intĂšgre lâĂ©quipe arriverait Ă comprendre lâarchitecture frontend en quelques minutes.
Est-ce que mon architecture est Ă©volutive ?
Je le disais en introduction, les besoins mĂ©tier Ă©voluent en permanence. Une feature en plus, une autre utilisĂ©e Ă plusieurs endroits qui devient un peu diffĂ©rente selon lĂ oĂč elle est consommĂ©e.
Le problĂšme câest que ni les devs ni les PO/PM ne voient dans le futur, donc oubliez tout de suite lâidĂ©e que vous ferez une architecture qui encaissera tous les changements futurs. Ca nâarrivera pas.
Alors on pourrait se dire que de sortir lâartillerie lourde dĂšs le dĂ©part est une bonne idĂ©e. Partons sur une architecture hexagonale en monorepo et full microfrontends. Au moins on aura quelque chose dâĂ©volutif.
Câest peut-ĂȘtre le cas, mais cela implique que lâĂ©quipe, incluant vous-mĂȘme, soit hautement qualifiĂ©s pour entretenir ce genre dâarchitecture. Et, spoiler alert, ça nâarrive pas non plus ou dans de trop rares cas.
Une architecture Ă©volutive est avant tout quelque chose de facilement incrĂ©mentale et dont on a la maĂźtrise. Câest Ă dire quelque chose qui diminue les contraintes : si on veut poser une nouvelle brique par dessus de maniĂšre isolĂ©e ou non, on peut le faire.
Si je devais donner un choix stratĂ©gique offrant beaucoup de valeurs sans demander une maturitĂ© technique dĂ©mesurĂ©e, câest lâutilisation dâun monorepo.
Un monorepo, câest le fait dâavoir un seul repository git avec plusieurs apps et libs. Cela permet notamment de faciliter le partage et la rĂ©utilisation de code entre vos diffĂ©rentes briques techniques et de profiter dâun Ă©cosystĂšme trĂšs Ă©volutif. Ma solution prĂ©fĂ©rĂ©e est NX.
NX est un Ă©cosystĂšme facilitant la mise en place et le maintient dâarchitecture web. Vous profiterez de ses outils qui permettent dâintĂ©grer facilement les outils et patterns populaires du web (Angular, React, Jest, Cypress, StorybookâŠ). NX est surtout utilisĂ© dans les architectures monorepo mais vous pouvez tout Ă fait utiliser NX pour une simple application.
Personnellement je lâutilise beaucoup dans mon quotidien et contrairement Ă ce quâil se dit ce nâest pas uniquement adaptĂ© aux gros projets. Mais ce nâest pas trivial non plus, cela nĂ©cessite une bonne coordination entre les membres de lâĂ©quipe.
Je prévois un article dédié sur le sujet ! Stay tuned!
A part ça, une erreur que je vois souvent et qui impacte nĂ©gativement lâĂ©volutivitĂ©, câest la mauvaise comprĂ©hension du principe DRY (Donât Repeat Yourself).
Jâai travaillĂ© pour une sociĂ©tĂ© qui fabriquait des logiciels en marque blanche. En gros, on avait plusieurs clients, chacun dĂ©sirant une application dĂ©diĂ©e avec certaines fonctionnalitĂ©s. Ces fonctionnalitĂ©s Ă©taient parfois les mĂȘmes entre les diffĂ©rentes apps (donc pour les diffĂ©rents clients), parfois non, ou parfois câĂ©tait les mĂȘmes mais avec quelques diffĂ©rences subtiles. Donc les apps avaient quelques bases communes entre elles mais aussi beaucoup de spĂ©cificitĂ©s.
Le choix (qui avait Ă©tĂ© fait bien avant mon arrivĂ©) a Ă©tĂ© de concevoir une seul app Angular avec toutes les fonctionnalitĂ©s Ă lâintĂ©rieur, chaque client partageait les mĂȘmes pages oĂč un tas de if se trouvaient et un fichier de configs par client qui pilotait lâactivation des fonctionnalitĂ©s Ă coup de shouldActivateThisFeature: true.
RĂ©sultat : on sâest retrouvĂ©s avec une app gigantesque avec des fichiers de 3k lignes qui contenaient les diffĂ©rentes logiques des diffĂ©rents clients. CâĂ©tait impossible Ă tester, trĂšs difficile Ă isoler et lâĂ©volution Ă©tait trĂšs limitĂ©e.
Comment on en est arrivĂ© lĂ ? Parce quâil y a une incomprĂ©hension sur la dĂ©finition de DRY.
Ce nâest pas parce que quelque chose Ă le mĂȘme nom que câest la mĂȘme chose.
Ici, les applications avaient le mĂȘme nom, les pages et fonctionnalitĂ©s aussi, donc on a considĂ©rĂ© que câĂ©tait la mĂȘme chose. On a donc tout mis dans une seule et mĂȘme app.
Une solution viable aurait Ă©tĂ© de faire une app distincte par client oĂč chacune dâentre elle dĂ©clarait son propre routing avec ses propres pages et de considĂ©rer chaque fonctionnalitĂ© comme une librairie consommable par les apps.
Pensez toujours Ă respecter une separation of concerns dans vos applications. Si deux features semblent trĂšs similaires mais divergent en quelques points, câest souvent le signal que ce sont en rĂ©alitĂ© deux features diffĂ©rentes.
Est-ce que mon architecture reflÚte mon besoin métier ?
Ce point lĂ est la rĂ©sultante des deux prĂ©cĂ©dents. En fait, une architecture qui reflĂšte le besoin mĂ©tier est la porte dâentrĂ©e vers une architecture agrĂ©able et Ă©volutive.
Ceci est lâun des principes du DDD (Domain Driven Design) : concevoir une application en suivant le contexte mĂ©tier. Cela veut dire que mon architecture est le reflet de mon business, si bien quâun PO/PM devrait pouvoir se balader dans mon repository et comprendre son architecture (jâexagĂšre un peu mais pas tant).
Il y a une phrase dâArnaud Lemaire que jâaime beaucoup et qui rĂ©sume une architecture DDD rĂ©ussie : des petits changements mĂ©tiers doivent entraĂźner des petits changements dans la codebase et des grands changements mĂ©tiers doivent entraĂźner des grands changements dans la codebase.
Bon, ceci Ă©tant dit, je ne ferai pas une explication exhaustive du DDD, dĂ©jĂ parce que ça pourrait tenir dans un bouquin, mais aussi parce que je ne prĂ©tends pas pouvoir le faire. En revanche, je peux vous expliquer comment appliquer certains de ses principes Ă la crĂ©ation dâarchitecture !
Il y a notamment deux voies pour mettre en place une architecture qui suit la logique métier :
- un découpage de dossiers qui suit la navigation utilisateur
- de la colocation de code
Le premier point consiste Ă avoir un folder routes
avec les différentes routes de votre applications (vos pages). Et dans chacune des routes on a potentiellement des sous routes. En somme, le code de la page domain.com/products-list
sera contenu dans routes/products-list
. Certains frameworks disposent de ce principe de maniĂšre naturelle, par exemple NextJS ou AnalogJS. Câest ce quâon appelle le file base routing. Câest trĂšs intĂ©ressant car ainsi la logique de notre structure de fichiers est liĂ©e et pilotĂ©e par le mĂ©tier, lâĂ©volution de notre codebase sera donc toujours fait au mĂȘme rythme que le mĂ©tier tout en ayant quelque chose de facilement comprĂ©hensible. Lâavantage est que si vous avez un ticket de bug sur une page, vous saurez immĂ©diatement oĂč intervenir.
Le second point concerne la colocation de code. Cela fait rĂ©fĂ©rence au fait de regrouper le code et les fichiers traitant du mĂȘme domaine mĂ©tier au mĂȘme niveau dans votre arborescence de fichiers Cela assure une comprĂ©hension claire des dĂ©pendances des diffĂ©rentes parties de votre code et des potentielles effets de bords.
Nous allons voir dans la section suivante des exemples dâarchitectures qui mettent en pratique tous les principes Ă©numĂ©rĂ©es jusquâici.
La mise en pratique
Je vous parlais de file base routing, voici comment faire lâĂ©quivalent dans Angular en partant dâune application en version 16 :
src/
app/
routes/
login/
login.route.ts
my-account/
profile/
profile.route.ts
purchase-history/
purchase-history.route.ts
my-account.route.ts
my-account.routing.ts
product/
product.route.ts
product-list/
product-list.route.ts
app.config.ts
app.component.ts
app.routing.ts
index.html
main.ts
angular.json
package.json
Ici, la structure de mes fichiers suit parfaitement la navigation de lâutilisateur dans lâapplication. Si je suis sur la page domain.com/product
alors je sais immĂ©diatement oĂč se trouve le code impliquĂ©. Et Ă lâintĂ©rieur de chaque folder on retrouve la route en question avec leurs sous routes Ă©ventuelles (câest le cas pour my-account
). Câest le fichier app.routing.ts
qui pilote le routing de premier niveau et celui de second niveau est géré par des fichiers comme my-account.routing.ts
par exemple.
Câest trĂšs pratique pour lâagrĂ©abilitĂ© comme je le disais plus haut, si vous avez un bug sur une page alors vous pourrez identifier en un clin dâĆil oĂč intervenir. Vous avez sans doute remarquĂ© quelque chose : je ne suis pas la convention Angular et je nomme mes routes en xxx.route.ts. Cela me permet dâidentifier rapidement ce quâest ce fichier plutĂŽt que de nommer tous mes composants xxx.component.ts. Je vous invite Ă faire de mĂȘme !
Maintenant, imaginez que vous avez un header et footer prĂ©sents sur toutes vos pages et que vous voulez en faire deux composants. Ces composants vont ĂȘtre utilisĂ©s dans le AppComponent
. Mais oĂč allez vous les placer dans votre architecture ?
La rĂ©ponse est : au plus proche de lĂ oĂč ils sont utilisĂ©s. Câest le principe de colocation de code !
src/
app/
routes/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
index.html
main.ts
angular.json
package.json
đ Les composants sont au mĂȘme niveau de leur endroit dâutilisation
Ainsi, des xxx.component.ts
vivant Ă cĂŽtĂ© dâun xxx.route.ts
signifient quâils sont utilisĂ© dedans. Tout simplement ! Pas besoin dâutiliser de dossier shared
ou core
dans ce cas-lĂ . Cela ne ferait que rajouter du bruit inutile.
Je suis cette mĂȘme logique pour toutes les routes et pas uniquement pour les composants :
src/
app/
routes/
login/
my-account/
profile/
purchase-history/
my-account.route.ts
my-account.routing.ts
sidebar.component.ts
product/
product-list/
product-list.route.ts
product-list.service.ts
product-list.store.ts
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Et oui ! Je parie que vous nâaviez jamais vu ce genre dâapproche avant. Et bien je peux vous assurer quâaprĂšs avoir essayĂ© des tas dâapproche, celle-ci apporte la meilleure DX au quotidien et sur le long terme.
Ici donc, tout est aplati, on minimise les dossiers et on suit une rÚgle simple : les fichiers qui sont utilisés ensemble sont cÎte à cÎte.
Par exemple, jâai besoin de crĂ©er un interceptor
pour ajouter mon token JWT Ă chaque requĂȘte HTTP. Cet interceptor
est utilisé dans mon app.config.ts
donc je le place Ă son niveau !
Il en va de mĂȘme pour les guards
, les services
etc.
Câest de la colocation de code. Le but est de comprendre en un clin dâĆil les responsabilitĂ©s et dĂ©pendances de mes fichiers.
Vous allez peut-ĂȘtre vous dire quâon va potentiellement avoir des dossiers avec 15 fichiers Ă lâintĂ©rieur, voire plus. En effet, et devinez quoi : câest vraiment pas grave, au contraire !
Croyez moi, les devs comprendront bien plus vite ce dĂ©coupage lĂ que dâobscurs dossiers core
ou autres qui entraĂźnent des rĂ©flexions supplĂ©mentaires et donc de potentielles erreurs et câest lĂ que la dette technique sâaccumule.
Prenez ces conseils comme des indications, si votre Ă©quipe se sent plus Ă lâaise avec un dossier par-ci par-lĂ alors faites le, mais gardez toujours en tĂȘte la DX et de rĂ©duire les chances dâaugmenter la dette technique.
Bon, on garde les fichiers en commun ensemble au mĂȘme endroit, trĂšs bien, mais quâen est-il des Ă©lĂ©ments partagĂ©s ?
Typiquement :
- des composants UI
- des features
- des utilitaires
Une solution est un dossier shared
au mĂȘme niveau que routes
qui contient les Ă©lĂ©ments partagĂ©s de lâapplication.
src/
app/
routes/
shared/
components/
features/
utils/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Ainsi en tant que dev, si un élément est réutilisé à plusieurs endroits alors je le mettrais ici.
Par exemples les composants :
src/
app/
routes/
shared/
components/
buttons/
cards/
form/
input/
textarea/
table/
features/
utils/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Ces composants représentent des éléments qui ne sont pas attachés à la logique métier exactement comme les composants de Angular Material par exemple.
Il existe des solutions encore plus puissantes comme les design system. Un design system gĂšre lâensemble de lâapparence de votre mĂ©tier, on y trouve les composants, les styles et les rĂšgles UI. Une mĂ©thodologie Ă©prouvĂ©e pour la crĂ©ation de design system est lâAtomic Design. Je nâen parlerai pas ici car ce nâest pas le sujet mais je vous invite Ă faire vos propres recherches !
Pour les utilitaires, rien de bien compliqué, on a des folders qui correspondent à nos besoins utilitaires :
src/
app/
routes/
shared/
components/
features/
utils/
converting/
formatting/
testing/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Pour les features, câest la mĂȘme idĂ©e. Une feature est un regroupement dâĂ©lĂ©ments mĂ©tiers rĂ©utilisables. Cela peut ĂȘtre des stores, des services, de la UI ou mĂȘme une combinaison de tout cela.
src/
app/
routes/
shared/
components/
features/
payment/
cart.component.ts
cart.store.ts
index.ts
payment.service.ts
user/
has-role.guard.ts
index.ts
has-logged-in.guard.ts
user.store.ts
utils/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Dans cette exemple nous avons deux features, payment et user. Chacun ayant un index.ts
pour exposer lâAPI aux consommateurs.
On peut facilement imaginer que dans mon application jâai besoin des informations du user
Ă plusieurs endroits, ainsi jâimporte ce dont jâai besoin depuis la feature user
. Il en va de mĂȘme pour payment
, le service pourrait exposer des méthodes de paiement déclenchable à plusieurs endroits de mon application, et on peut imaginer que le cart
soit affichable Ă plusieurs endroits Ă©galement.
Important : entendons nous bien, je possĂšde un folder shared
mais pas de SharedModule
ou que sais-je encore. Le folder shared apporte lâindication que les Ă©lĂ©ments Ă lâintĂ©rieur sont rĂ©utilisĂ©s Ă plusieurs endroits, mais pas quâils sont tous packagĂ©s ensemble ! Ils ont tous leurs propres responsabilitĂ©s et ne communique pas ensemble, comme les Ă©lĂ©ments dans routes
par exemple.
Bref, chaque feature est un sous-ensemble de mon domaine dâapplication et contient des Ă©lĂ©ments rĂ©utilisables. Dans lâidĂ©e si une fonctionnalitĂ© Ă©merge pour une seule page de lâapplication alors elle ne devrait pas ĂȘtre dans shared/features/xxx
. Par exemple si un administrateur à la possibilité de supprimer un user en faisant un http.delete
sur un endpoint user/${id}
via une page admin/delete-users
alors en thĂ©orie cette logique devrait ĂȘtre contenue dans routes/admin/delete-users/delete-users.service.ts
. Donc pas dans la feature user
car cette fonctionnalitĂ© nâest pas partagĂ©e.
Je dis bien âen thĂ©orieâ car câest typiquement sur ce genre de choix oĂč on va adapter son architecture selon le mĂ©tier et les prĂ©fĂ©rences de lâĂ©quipe, on pourrait prĂ©fĂ©rer mettre cette logiquement Ă©galement dans la feature user pour certaines raisons. Et en poussant lâidĂ©e un peu plus loin, on pourrait mĂȘme avoir ceci :
src/
app/
design-system/
components/
buttons/
cards/
form/
table/
styles/
features/
payment/
user/
routes/
login/
my-account/
product/
product-list/
utils/
converting/
formatting/
testing/
app.config.ts
app.component.ts
app.routing.ts
footer.component.ts
header.component.ts
initializer.service.ts
jwt.interceptor.ts
index.html
main.ts
angular.json
package.json
Ici, jâai carrĂ©ment fait sauter la notion de shared
. Les composants sont déplacés dans un design-system
, utils
est directement Ă la racine ainsi que features
signifiant quâils pourraient ĂȘtre partagĂ©s ou non.
Câest, Ă mon sens, une architecture trĂšs solide (hormis monorepo) si lâĂ©quipe Ă un haut niveau de maturitĂ©. Le risque rĂ©side dans le fait de perturber lâagrĂ©abilitĂ© au quotidien, jâimagine facilement quâune feature devienne mal isolĂ©e et quâon y introduise des notions qui ne devraient pas y ĂȘtre. PrĂ©fĂ©rez la colocation de code tant que câest possible.
Pour aller plus loin
Il y a beaucoup de mĂ©thodologies dont je nâai pas parlĂ© :
- microfrontends
- clean architecture
- architecture hexagonale
Ce sont des sujets poussĂ©s et qui nĂ©cessite un haut niveau de maturitĂ© technique mais qui peuvent apporter beaucoup Ă la pĂ©rennitĂ© de vos projets. Assurez vous de maĂźtriser les points prĂ©cĂ©dents avant pour comprendre lâessence dâune bonne architecture avant de vous lancer dans ces recherches.
Résumé
- Pensez toujours à vos collÚgues qui récupÚreront votre code en priorisant la DX
- Si je reçois un ticket qui me dit âil y a un bug sur cette featureâ, je dois trouver oĂč intervenir en quelques secondes
- Si une nouvelle personne intĂšgre lâĂ©quipe (et quelque soit son niveau de sĂ©nioritĂ© Ă peu de chose prĂšs) alors elle doit comprendre lâarchitecture en quelques minutes
- Lâorganisation de vos fichiers doit ĂȘtre logique et consistant
- MĂ©fiez vous du DRY qui est parfois trompeur
- Mettez un folder routes qui suivra la navigation de lâutilisateur
- Aplatissez vos fichiers et utilisez de la colocation de code au maximum tant que cela respecte la separation of concerns
- Utilisez un dossier shared pour y mettre des sous dossiers components, utils, features etc contenant vos éléments partagés
- Alternativement, vous pouvez mettre ces sous dossiers directement Ă la racine au mĂȘme niveau que routes
- ConsidĂ©rez sĂ©rieusement lâapproche monorepo avec NX
Conclusion
Une architecture parfaite pour votre business mettra du temps Ă ĂȘtre mise en place. Il faudra faire, dĂ©faire et refaire rĂ©guliĂšrement. Mais si vous suivez ces conseils et que vous priorisez la DX alors tout ce passera pour le mieux et vos collĂšgues et clients/responsables vous remercieront !