Qu'est-ce que l'architecture modulaire ? Passons en revue un exemple de ce que ce n'est pas, et
nous travaillerons à sa transformation. À la fin, vous en serez peut-être convaincu
mérite ou que c'est une perte de temps colossale.
Il s'agit d'un véritable scénario que j'ai vécu avec un esprit de croissance au travail. Noms et détails
anonymisé, mais un exemple concret d'un concept commun devrait être amusant à marcher
à travers, au moins.
Notre site dispose d'un bouton qui se trouve dans l'en-tête du site. Il affiche combien
V-Bucks que l'utilisateur a laissés, mais qui contiennent également une certaine logique métier :
Et ainsi de suite. Il existe de nombreux cas de ce type nos chefs de produits et chefs de projet
et les responsables de la conception et les directeurs du groupe V-Bucks ont imaginé que nous devions
poignée.
Jimbo, le stagiaire, a été chargé de mettre en œuvre cela car c'est juste un
bouton !
Il passe au crible quinze itérations contradictoires de conceptions Figma. Il trouve
exigences dans autant de documents Word distincts qu’il y a de MP. Il organise et
endure sept séances de transfert de connaissances avec sept équipes pour découvrir le
connaissance ancienne et exclusive des services qui lui fourniront les données dont il a besoin
pour le type d’utilisateur et le nombre de V-Bucks. L'équipe de contenu l'a rassuré sur le fait que le
la version finale de toutes les chaînes sera approuvée par les services juridiques et marketing d'ici la fin
de la semaine, et avec ça, il est prêt à construire ce bouton.
Voici la première itération de son bouton V-Bucks, ses popovers et pertinents
logique métier.
Jimbo est satisfait de la structure de répertoires simple qu'il a proposée :
/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
Alors il commence à construire ce bouton, et cela commence assez innocemment.
export const VBucksButton: React.FC<VBBProps> = ({ ... }) => { // Set up state const authConfig = { ... } const { user } = useAuth({ ...authConfig }) const { vBucks } = useGetVBucks({ user }) const { telemetry } = useTelemetry() const { t } = useTranslation('vBucksButton.content') const styles = useButtonStyles() // Derive some state via business logic const handleClick = () => { ... } const buttonText = vBucks === ERROR ? '--' : vBucks.toString(); // About 25 more lines of various button state, error handling, // conditional rendering, with comments throughout explaining // why we're showing or hiding something or other const popoverProps = { // About 200 lines of typical popover handling, // telemetry, business logic, content to display, etc } const tooltipProps = { // Another 100 lines of the same for tooltip } return ( <VBucksPopover {...popoverProps} trigger={ <Tooltip {...tooltipProps}> <button ariaLabel={t('ariaLabel')} className={` about seven-hundred classnames for responsive design, accessibility, conditional premium styles, et cetera`} onClick={handleClick}> {buttonText} </button> </Tooltip> } /> ) } <p>Il a mis en œuvre un premier essai. Le VBucksPopover a un complexe similaire<br> logique métier, gestion des erreurs, gestion de l'état, style et commentaires d'excuse<br> dette technologique au nom de l'expédition.</p> <p>Avec un peu moins de 400 lignes, ce bouton est d'une simplicité triviale. Même si le popover est<br> encore 500 lignes de spaghetti. Est-ce que le "nettoyer" ou le diviser vraiment<br> profite-t-il à nous, ou à nos utilisateurs, de quelque manière que ce soit ? Ça dépend. Si c'est tout ce dont nous avons besoin<br> ce bouton, peu importe. Passons à autre chose !</p> <p>Mais deux mois se sont écoulés et un PM et un designer d'une autre équipe produit adorent<br> votre bouton et le souhaitez dans l'en-tête de leur application. Ils ont une liste <em>simple</em>, non<br> la pression de leur côté, de certains changements qu'ils aimeraient que vous acceptiez et si<br> vous pourriez s'il vous plaît donner une ETA d'ici la fin de la journée pour LT, ce serait génial, merci :</p> <ul> <li>Mettez à jour le style du bouton et affichez le texte en fonction de l'application dans laquelle il est affiché</li> <li>Afficher un ensemble de popovers complètement différent, par application</li> <li>Ouvrez un nouveau modal de vente incitative standard à l'échelle de l'entreprise lorsque l'utilisateur n'a plus de V-Bucks, mais uniquement dans certaines régions et uniquement pour les utilisateurs âgés de 16 ans et uniquement s'ils se trouvent dans groupe d'expérimentation A</li> </ul> <p>Jimbo peut-il regrouper toutes ces nouvelles fonctionnalités dans les mêmes composants ?<br><br> Oui. Le fractionnement ou le refactoring profiteront-ils aux utilisateurs ou impressionneront-ils vos managers ?<br><br> Non, mais le refactoring a de solides arguments à ce niveau de complexité :</p> <ul> <li>Santé mentale des développeurs</li> <li>La santé mentale du développeur qui remplace Jimbo lorsqu'il est accusé de ne pas avoir refactorisé</li> <li>Plus de répétitions, pour faire mieux dès le début la prochaine fois</li> <li>Quelque chose sur lequel bloguer plus tard</li> </ul> <h3> L’approche de l’architecture modulaire </h3> <p>La morale des initiés du Clean Code et des autres types anaux qui en savent assez pour<br> répondez régulièrement sur Stack Overflow, et même vos grands-parents, regardez quelque chose<br> comme ça :</p> <ul> <li>KISS, DRY et autres couvertures avec acronymes</li> <li>Séparation des préoccupations</li> <li>Atomicité ! Découplage ! Onomatopée !</li> </ul> <p>Ceux-ci sont géniaux et aident à éclairer la prochaine tentative de Jimbo. Il n'a pas reçu de PIP après<br> tout cela, et j'ai en fait reçu une promotion pour avoir livré plus tôt que prévu et pour avoir partagé<br> tant de rencontres et de documents.<br><br> Mais il est plus sage maintenant et a appris une façon sympa de mettre en œuvre ces adages. On dirait<br> quelque chose comme ça :<br> </p> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
On dirait des tonnes de passe-partout pour un bouton et un popover. Pourquoi serait-ce
mieux ?
Ça dépend. Voici le bref aperçu de Jimbo avec justification :
C'est évolutif à l'infini ! Ces éléments de base ne sont pas décomposés par
des règles arbitraires comme des lignes de code ou de la « complexité ». Ils sont répartis par
objectif : chaque frontière conceptuelle sert un seul objectif.
Un MP souhaite que vous fassiez 10 nouveaux popovers ? Pas de problème : l'architecture de Jimbo peut
gérer ça.
La direction veut de meilleures statistiques sur les ventes dans certaines applications, mais d'autres équipes ne le font pas
avoir le financement nécessaire pour développer la télémétrie à l’appui de cela. Super! Nous avons
utilitaires de télémétrie que nous pouvons mettre à l'échelle horizontalement pour répondre à divers changements
exigences.
Une refonte radicale signifie que chaque popover doit afficher des éléments différents,
en fonction de conditions différentes. C'est généralement beaucoup plus simple maintenant que tous les
les éléments que nous rendons, et toute la logique que nous utilisons pour les restituer, existent dans des formats bien définis
blocs. Ils ne sont plus mélangés dans un tas géant de conflits et de logique
chaînes de 20 lignes de long.
Voici un exemple de ce modèle de conteneur/rendu :
/v-bucks-button ├── button.tsx ├── index.ts └── /v-bucks-popover │ ├── popover.tsx
export const VBucksButton: React.FC<VBBProps> = ({ ... }) => { // Set up state const authConfig = { ... } const { user } = useAuth({ ...authConfig }) const { vBucks } = useGetVBucks({ user }) const { telemetry } = useTelemetry() const { t } = useTranslation('vBucksButton.content') const styles = useButtonStyles() // Derive some state via business logic const handleClick = () => { ... } const buttonText = vBucks === ERROR ? '--' : vBucks.toString(); // About 25 more lines of various button state, error handling, // conditional rendering, with comments throughout explaining // why we're showing or hiding something or other const popoverProps = { // About 200 lines of typical popover handling, // telemetry, business logic, content to display, etc } const tooltipProps = { // Another 100 lines of the same for tooltip } return ( <VBucksPopover {...popoverProps} trigger={ <Tooltip {...tooltipProps}> <button ariaLabel={t('ariaLabel')} className={` about seven-hundred classnames for responsive design, accessibility, conditional premium styles, et cetera`} onClick={handleClick}> {buttonText} </button> </Tooltip> } /> ) }
/vBucksButton ├── /hooks │ ├── index.ts │ └── useButtonState.hook.ts ├── /vBucksPopover │ ├── /app1Popover │ │ ├── /hooks │ │ │ ├── index.ts │ │ │ └── usePopoverState.hook.ts │ │ ├── ... │ ├── /app2Popover │ ├── index.ts │ ├── popover.renderer.tsx │ ├── popover.styles.ts │ ├── popover.tsx │ └── popover.types.ts ├── /utils │ ├── experimentation.util.ts │ ├── store.util.ts │ ├── telemetry.util.ts │ └── vBucks.businessLogic.util.ts ├── button.renderer.tsx ├── button.styles.ts ├── button.tsx ├── button.types.ts └── index.ts
À part : Les documents TailwindCSS recommandent explicitement de ne pas utiliser @apply pour extraire des classes courantes comme celle-ci. Cela entraîne une différence presque nulle dans la taille du bundle, et aucune autre différence que celle-là, à part "vous devez trouver des noms de classe". Le CSS de qualité production finit presque toujours par comporter des dizaines de lignes, multipliées par le nombre d'éléments nécessitant un style dans un composant donné. Ce compromis semble en valoir la peine dans 90 % des cas.
Et le reste de la logique métier existante et nouvelle réside dans les hooks et les utilitaires !
Cette nouvelle architecture satisfait les fanatiques et facilite la mise à l'échelle ou
supprimer ou déplacer.
L'écriture de tests unitaires devient moins pénible car vous avez des éléments bien définis
frontières. Votre moteur de rendu n'a plus besoin de se moquer de dix services différents pour
validez qu'il montre un ensemble de brillants compte tenu de certaines entrées. Vos crochets peuvent
testez, isolément, qu'ils correspondent à votre logique métier prévue.
L’ensemble de votre couche d’état vient-il de changer ? Ce serait dommage si le code dans votre
le hook était étroitement couplé au code qui l'utilise, mais maintenant c'est plus simple
changement et votre moteur de rendu attend toujours juste une entrée.
Cette architecture modulaire ajoute beaucoup de passe-partout et peut finalement fournir
aucun avantage.
Je ne peux pratiquement pas le recommander si vous travaillez sur un projet passionnant ou
donner la priorité à l’expédition et à la fourniture de valeur avant tout. Si tu as quelque chose qui
il semble que sa portée pourrait s'élargir avec le temps, ou que vous souhaiterez peut-être le faire
refonte complète après un POC, cela peut réduire la dette technologique... parfois.
Vous pouvez utiliser des outils comme Plop pour générer ce passe-partout.
Alors, qu'ai-je vraiment appris du travail et de l'architecture modulaire de Jimbo ?
Clean Code et acronymes que nous apprenons à l'école et les Well Ackshuallys du monde
sont une extrémité d’un spectre. Pirater ensemble du code spaghetti fonctionnel en est une autre
fin, et fonctionne souvent très bien, car en fin de compte, tout code est une dette technologique.
La meilleure solution existe dans un état quantique ou une combinaison de ces extrémités, et
le chemin que nous choisirons sera probablement décidé en fonction de :
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!