L'auteur du matériel, dont nous publions la traduction aujourd'hui, dit que le
référentiel GitHub , sur lequel il a travaillé et plusieurs autres pigistes, a reçu, pour diverses raisons, environ 8 200 étoiles en 3 jours. Ce référentiel est arrivé en premiÚre place sur HackerNews et GitHub Trending, et 20 000 utilisateurs de Reddit l'ont voté.

Ce référentiel reflÚte la méthodologie de développement d'applications full-stack à laquelle cet article est consacré.
Contexte
J'allais écrire ce matériel pendant un certain temps. Je pense que je ne peux pas trouver de meilleur moment que celui-ci lorsque notre référentiel est trÚs populaire.
N ° 1 sur les tendances GitHubJe travaille dans une équipe de
pigistes . Nos projets utilisent React / React Native, NodeJS et GraphQL. Ce matériel est destiné à ceux qui veulent en savoir plus sur la façon dont nous développons des applications. De plus, il sera utile à ceux qui rejoindront notre équipe à l'avenir.
Je vais maintenant parler des principes de base que nous utilisons lors du développement de projets.
Plus c'est simple, mieux c'est.
«Plus c'est simple, mieux c'est» est plus facile à dire qu'à faire. La plupart des développeurs sont conscients que la simplicité est un principe important dans le développement de logiciels. Mais ce principe n'est pas toujours facile à suivre. Si le code est simple, cela facilite le support du projet et simplifie le travail d'équipe sur ce projet. De plus, le respect de ce principe aide à travailler avec du code écrit, disons, il y a six mois.
Voici les erreurs que j'ai rencontrées concernant ce principe:
- DĂ©sir injustifiĂ© de respecter le principe DRY. Parfois, copier et coller du code est tout Ă fait normal. Pas besoin de rĂ©sumer tous les 2 fragments de code qui sont quelque peu similaires les uns aux autres. J'ai moi-mĂȘme fait cette erreur. Tous l'ont peut-ĂȘtre commis. DRY est une bonne approche de programmation, mais le choix d'une abstraction ayant Ă©chouĂ© ne peut qu'aggraver la situation et compliquer la base de code. Si vous voulez en savoir plus sur ces idĂ©es, je vous recommande de lire la programmation AHA de Kent A Dodds.
- Refus d'utiliser les outils disponibles. Un exemple de cette erreur est l'utilisation de
reduce
au lieu de map
ou de filter
. Bien sûr, l'utilisation de reduce
peut reproduire le comportement de la map
. Mais cela est susceptible de conduire Ă une augmentation de la taille du code, et au fait qu'il sera plus difficile pour d'autres personnes de comprendre ce code, Ă©tant donnĂ© que la «simplicitĂ© du code» est un concept subjectif. Parfois, il peut ĂȘtre nĂ©cessaire d'utiliser simplement reduce
. Et si vous comparez la vitesse de traitement de l'ensemble de données à l'aide des appels de map
et de filter
combinés dans une chaßne, et à l'aide de reduce
, il s'avĂšre que la deuxiĂšme option fonctionne plus rapidement. Dans l'option avec reduce
vous devez regarder l'ensemble des valeurs une fois, pas deux. Nous avons devant nous un débat sur la productivité et la simplicité. Dans la plupart des cas, je préférerais la simplicité et j'éviterais une optimisation prématurée du code, c'est-à -dire que je choisirais une paire map
/ filter
au lieu de reduce
. Et s'il s'avérait que la construction de la map
et du filter
devenait le goulot d'étranglement du systÚme, cela traduirait le code à reduce
.
De nombreuses idées qui seront discutées ci-dessous visent à rendre la base de code aussi simple que possible et à la maintenir dans cet état.
Gardez des entités similaires proches les unes des autres
Ce principe, le «principe de colocation», s'applique à de nombreuses parties de la demande. C'est la structure des dossiers dans lesquels le code client et serveur est stocké, c'est le stockage du code de projet dans un référentiel, c'est aussi la prise de décision concernant le code dans un certain fichier.
â RĂ©fĂ©rentiel
Il est recommandĂ© de conserver le code client et serveur dans le mĂȘme rĂ©fĂ©rentiel. C'est simple. Ne compliquez pas ce qui n'est pas nĂ©cessaire pour compliquer. Avec cette approche, il est pratique d'organiser un travail d'Ă©quipe coordonnĂ© sur un projet. J'ai travaillĂ© sur des projets utilisant diffĂ©rents rĂ©fĂ©rentiels pour stocker des matĂ©riaux. Ce n'est pas un dĂ©sastre, mais les mono-dĂ©pĂŽts facilitent la vie.
â Structure du projet de la partie client de l'application
Nous écrivons des applications complÚtes. Autrement dit, le code client et le code serveur. La structure de dossiers d'un projet client typique fournit des répertoires distincts pour les composants, les conteneurs, les actions, les réducteurs et les itinéraires.
Des actions et des rĂ©ducteurs sont prĂ©sents dans les projets qui utilisent Redux. Je m'efforce de me passer de cette bibliothĂšque. Je suis sĂ»r qu'il existe des projets de haute qualitĂ© qui utilisent la mĂȘme structure. Certains de mes projets ont des dossiers sĂ©parĂ©s pour les composants et les conteneurs. Un dossier de composants peut stocker quelque chose comme des fichiers avec du code pour des entitĂ©s telles que
BlogPost
et
Profile
. Dans le dossier du conteneur, il existe des fichiers qui stockent le code du conteneur
BlogPostContainer
et
ProfileContainer
. Le conteneur reçoit des données du serveur et les transmet au composant enfant "stupide", dont la tùche est d'afficher ces données à l'écran.
Il s'agit d'une structure de travail. C'est au moins homogÚne, et c'est trÚs important. Cela conduit au fait que le développeur, qui a rejoint le travail sur le projet, comprendra rapidement ce qui s'y passe et quel rÎle jouent ses différentes parties. L'inconvénient de cette approche, en raison de laquelle j'ai récemment essayé de ne pas l'utiliser, est qu'elle oblige le programmeur à se déplacer constamment dans la base de code. Par exemple, les
BlogPostContainer
ProfileContainer
et
BlogPostContainer
n'ont rien en commun, mais leurs fichiers sont cÎte à cÎte et loin des fichiers dans lesquels ils sont réellement utilisés.
Depuis quelque temps, je m'efforce de placer des fichiers dont le contenu est prĂ©vu d'ĂȘtre partagĂ© dans les mĂȘmes dossiers. Cette approche de la structuration des projets repose sur le regroupement des fichiers en fonction de leurs capacitĂ©s. GrĂące Ă cette approche, vous pouvez grandement vous simplifier la vie si, par exemple, vous placez le composant parent et son composant enfant «stupide» dans le mĂȘme dossier.
Habituellement, nous utilisons le dossier
routes
/
screens
et le dossier des
components
. Le dossier des composants stocke généralement du code pour des éléments tels que
Button
ou
Input
. Ce code peut ĂȘtre utilisĂ© sur n'importe quelle page de l'application. Chaque dossier du dossier des itinĂ©raires est une page d'application distincte. Dans le mĂȘme temps, les fichiers avec le code du composant et le code logique d'application liĂ©s Ă cette route se trouvent dans le mĂȘme dossier. Et le code des composants utilisĂ©s sur plusieurs pages tombe dans le dossier des
components
.
Dans le dossier de routage, vous pouvez crĂ©er des dossiers supplĂ©mentaires dans lesquels le code responsable de la formation des diffĂ©rentes parties de la page est regroupĂ©. Cela est logique dans les cas oĂč l'itinĂ©raire est reprĂ©sentĂ© par une grande quantitĂ© de code. Ici, cependant, je voudrais avertir le lecteur qu'il ne vaut pas la peine de crĂ©er des structures Ă partir de dossiers avec un niveau d'imbrication trĂšs Ă©levĂ©. Cela complique le mouvement du projet. Les structures de dossiers imbriquĂ©es profondes sont l'un des signes de la complexitĂ© excessive d'un projet. Il convient de noter que l'utilisation d'outils spĂ©cialisĂ©s, tels que les commandes de recherche, donne au programmeur des outils pratiques pour travailler avec le code du projet et pour trouver ce dont il a besoin. Mais la structure des fichiers du projet affecte Ă©galement sa convivialitĂ©.
En structurant le code du projet, vous pouvez regrouper des fichiers en fonction non pas de l'itinĂ©raire, mais des capacitĂ©s du projet implĂ©mentĂ©es par ces fichiers. Dans mon cas, cette approche se montre parfaitement sur des projets d'une seule page qui implĂ©mentent de nombreuses fonctionnalitĂ©s sur leur seule page. Mais il convient de noter que le regroupement des matĂ©riaux du projet par itinĂ©raire est plus facile. Cette approche ne nĂ©cessite pas d'efforts mentaux particuliers pour dĂ©cider quelles entitĂ©s doivent ĂȘtre placĂ©es les unes Ă cĂŽtĂ© des autres et pour rechercher quelque chose.
Si nous allons plus loin dans le regroupement du code, nous pouvons dĂ©cider que le code des conteneurs et des composants sera Ă juste titre placĂ© dans le mĂȘme fichier. Et vous pouvez aller encore plus loin: mettez le code de deux composants dans un seul fichier. Je suppose que vous pensez peut-ĂȘtre maintenant que recommander de telles choses est vraiment un blasphĂšme. Mais en rĂ©alitĂ©, tout est loin d'ĂȘtre si mauvais. En fait, cette approche est pleinement justifiĂ©e. Et si vous utilisez des hooks React, ou du code gĂ©nĂ©rĂ© (ou les deux), je recommanderais cette approche.
En fait, la question de savoir comment dĂ©composer le code en fichiers n'est pas d'une importance capitale. La vraie question est de savoir pourquoi vous devrez peut-ĂȘtre diviser les composants en intelligents et stupides. Quels sont les avantages de cette sĂ©paration? Il y a plusieurs rĂ©ponses Ă cette question:
- Les applications ainsi conçues sont plus faciles à tester.
- Le développement de telles applications facilite l'utilisation d'outils comme le Storybook.
- Les composants stupides peuvent ĂȘtre utilisĂ©s avec de nombreux composants intelligents diffĂ©rents (et vice versa).
- Les composants intelligents peuvent ĂȘtre utilisĂ©s sur diffĂ©rentes plateformes (par exemple, sur les plateformes React et React Native).
Tous ces arguments sont réels en faveur de la division des composants en «intelligent» et «stupide», mais ils ne sont pas applicables à toutes les situations. Par exemple, nous utilisons souvent
Apollo Client avec des crochets lors de la crĂ©ation de projets. Afin de tester de tels projets, vous pouvez crĂ©er des simulations de rĂ©ponse Apollo ou des simulateurs de crochet. Il en va de mĂȘme pour le livre de contes. Si nous parlons de mĂ©langer et de partager des composants «intelligents» et «stupides», alors, en fait, je ne l'ai jamais rencontrĂ© dans la pratique. Concernant l'utilisation multiplateforme du code, il y avait un projet dans lequel j'allais faire quelque chose de similaire, mais je ne l'ai jamais fait. C'Ă©tait censĂ© ĂȘtre le
mono-référentiel Lerna. De nos jours, au lieu de cette approche, vous pouvez opter pour React Native Web.
En consĂ©quence, nous pouvons dire que la sĂ©paration des composants en «intelligent» et «idiot» a une certaine signification. Il s'agit d'un concept important qui mĂ©rite d'ĂȘtre connu. Mais souvent, vous n'avez pas Ă vous en prĂ©occuper, surtout compte tenu de l'apparition rĂ©cente des crochets React.
Le point fort de la combinaison des capacitĂ©s des composants «intelligents» et «idiots» dans une mĂȘme entitĂ© est qu'elle accĂ©lĂšre le dĂ©veloppement et simplifie la structure du code.
De plus, si un tel besoin se fait sentir, un composant peut toujours ĂȘtre divisĂ© en deux composants distincts - «intelligent» et «stupide».
Stylisation
Nous utilisons
des composants émotion /
style pour les applications de style. Il y a toujours la tentation de séparer les styles dans un fichier séparé. J'ai vu certains développeurs faire cela. Mais, aprÚs avoir essayé les deux approches, je n'ai finalement pas trouvé de raison de déplacer les styles vers un fichier séparé. Comme dans le cas de beaucoup d'autres choses, dont nous parlons ici, un développeur peut lui faciliter la vie en combinant les styles et les composants auxquels ils se rapportent dans un seul fichier.
Structure Structure du projet de la partie serveur de l'application
Tout ce qui prĂ©cĂšde est vrai en ce qui concerne la structuration du code cĂŽtĂ© serveur de l'application. Une structure typique que j'essaie personnellement d'Ă©viter pourrait ressembler Ă
ceci :
src â app.js # ââââapi # Express ââââconfig # ââââjobs # agenda.js ââââloaders # ââââmodels # ââââservices # - ââââsubscribers # ââââtypes # (d.ts) Typescript
Nous utilisons gĂ©nĂ©ralement GraphQL dans nos projets. Par consĂ©quent, ils utilisent des fichiers qui stockent des modĂšles, des services et des outils de reconnaissance. Au lieu de les disperser Ă diffĂ©rents endroits du projet, je les rassemble dans un dossier. Le plus souvent, ces fichiers seront partagĂ©s, et il sera plus facile de travailler avec eux s'ils sont stockĂ©s dans le mĂȘme dossier.
N'écrasez pas les définitions de type plusieurs fois
Nous utilisons de nombreuses solutions dans nos projets qui sont en quelque sorte liĂ©es aux types de donnĂ©es. Ce sont TypeScript, GraphQL, les schĂ©mas de base de donnĂ©es et parfois MobX. En consĂ©quence, il peut s'avĂ©rer que les types pour les mĂȘmes entitĂ©s sont dĂ©crits 3-4 fois. De telles choses devraient ĂȘtre Ă©vitĂ©es. Nous devons nous efforcer d'utiliser des outils qui gĂ©nĂšrent automatiquement des descriptions de type.
Sur le serveur, une combinaison de TypeORM / Typegoose et TypeGraphQL peut ĂȘtre utilisĂ©e Ă cet effet. Cela suffit pour dĂ©crire tous les types utilisĂ©s. TypeORM / Typegoose vous permet de dĂ©crire le schĂ©ma de base de donnĂ©es et les types TypeScript correspondants. TypeGraphQL vous aidera Ă crĂ©er les types de GraphQL et TypeScript.
Voici un exemple de détermination des types de TypeORM (MongoDB) et TypeGraphQL dans un fichier:
import { Field, ObjectType, ID } from 'type-graphql' import { Entity, ObjectIdColumn, ObjectID, Column, CreateDateColumn, UpdateDateColumn, } from 'typeorm' @ObjectType() @Entity() export default class Policy { @Field(type => ID) @ObjectIdColumn() _id: ObjectID @Field() @CreateDateColumn({ type: 'timestamp' }) createdAt: Date @Field({ nullable: true }) @UpdateDateColumn({ type: 'timestamp', nullable: true }) updatedAt?: Date @Field() @Column() name: string @Field() @Column() version: number }
Le générateur de code GraphQL peut également générer de nombreux types différents. Nous utilisons cet outil pour créer des types TypeScript sur le client, ainsi que des hooks React qui accÚdent au serveur.
Si vous utilisez MobX pour contrĂŽler l'Ă©tat de l'application, puis en utilisant quelques lignes de code, vous pouvez obtenir des types TS gĂ©nĂ©rĂ©s automatiquement. Si vous utilisez Ă©galement GraphQL, vous devriez jeter un Ćil au nouveau package -
MST-GQL , qui génÚre une arborescence d'état à partir du schéma GQL.
L'utilisation de ces outils ensemble vous évitera de réécrire de grandes quantités de code et vous aidera à éviter les erreurs courantes.
D'autres solutions, telles que
Prisma ,
Hasura et
AWS AppSync , peuvent également aider à éviter les déclarations de type en double. Bien entendu, l'utilisation de tels outils a ses avantages et ses inconvénients. Dans les projets que nous créons, de tels outils ne sont pas toujours utilisés, car nous devons déployer le code sur nos propres serveurs d'organisations.
Dans la mesure du possible, recourir à des moyens de génération automatique de code
Si vous regardez le code que vous crĂ©ez sans utiliser les outils ci-dessus pour gĂ©nĂ©rer automatiquement du code, il s'avĂšre que les programmeurs doivent constamment Ă©crire la mĂȘme chose. Le principal conseil que je peux donner Ă ce sujet est que vous devez crĂ©er des extraits pour tout ce que vous utilisez souvent. Si vous entrez souvent la commande
console.log
, créez un extrait comme
cl
, qui se transforme automatiquement en
console.log()
. Si vous ne le faites pas et que vous me demandez de vous aider avec le débogage du code, cela me dérangera beaucoup.
Il existe de nombreux packages avec des extraits, mais il est facile de créer vos propres extraits. Par exemple, en utilisant le
générateur d'extraits .
Voici le code qui me permet d'ajouter certains de mes extraits préférés à VS Code:
{ "Export default": { "scope": "javascript,typescript,javascriptreact,typescriptreact", "prefix": "eid", "body": [ "export { default } from './${TM_DIRECTORY/.*[\\/](.*)$$/$1/}'", "$2" ], "description": "Import and export default in a single line" }, "Filename": { "prefix": "fn", "body": ["${TM_FILENAME_BASE}"], "description": "Print filename" }, "Import emotion styled": { "prefix": "imes", "body": ["import styled from '@emotion/styled'"], "description": "Import Emotion js as styled" }, "Import emotion css only": { "prefix": "imec", "body": ["import { css } from '@emotion/styled'"], "description": "Import Emotion css only" }, "Import emotion styled and css only": { "prefix": "imesc", "body": ["import styled, { css } from ''@emotion/styled'"], "description": "Import Emotion js and css" }, "Styled component": { "prefix": "sc", "body": ["const ${1} = styled.${2}`", " ${3}", "`"], "description": "Import Emotion js and css" }, "TypeScript React Function Component": { "prefix": "rfc", "body": [ "import React from 'react'", "", "interface ${1:ComponentName}Props {", "}", "", "const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = props => {", " return (", " <div>", " ${1:ComponentName}", " </div>", " )", "}", "", "export default ${1:ComponentName}", "" ], "description": "TypeScript React Function Component" } }
En plus des extraits, les gĂ©nĂ©rateurs de code peuvent aider Ă gagner du temps. Vous pouvez les crĂ©er vous-mĂȘme. J'aime utiliser
plop pour cela.
Angular a ses propres gĂ©nĂ©rateurs de code intĂ©grĂ©s. Ă l'aide des outils de ligne de commande, vous pouvez crĂ©er un nouveau composant, composĂ© de 4 fichiers, qui prĂ©sente tout ce que vous pouvez espĂ©rer trouver dans le composant. Il est dommage que React ne dispose pas d'une telle fonctionnalitĂ© standard, mais quelque chose de similaire peut ĂȘtre créé indĂ©pendamment en utilisant plop. Si chaque nouveau composant que vous crĂ©ez doit ĂȘtre prĂ©sentĂ© sous la forme d'un dossier contenant un fichier avec le code du composant, un fichier de test et un fichier Storybook, le gĂ©nĂ©rateur vous aidera Ă crĂ©er tout cela avec une seule commande. Dans de nombreux cas, cela facilite considĂ©rablement la vie du dĂ©veloppeur. Par exemple, lorsque vous ajoutez une nouvelle fonctionnalitĂ© au serveur, exĂ©cutez simplement une commande sur la ligne de commande. AprĂšs cela, des fichiers de l'entitĂ©, des services et des modules de reconnaissance seront automatiquement créés contenant toutes les structures de base nĂ©cessaires.
Une autre force des gĂ©nĂ©rateurs de code est qu'ils contribuent Ă l'uniformitĂ© du dĂ©veloppement d'Ă©quipe. Si tout le monde utilise le mĂȘme gĂ©nĂ©rateur de plop, alors le code sera trĂšs uniforme pour tout le monde.
Formatage automatique du code
Le formatage du code est une tùche simple, mais, malheureusement, il n'est pas toujours résolu correctement. Ne perdez pas de temps à aligner manuellement le code ou à y insérer des points-virgules. Utilisez
Prettier pour formater automatiquement le code lors de la
validation .
Résumé
Dans cet article, je vous ai parlé de certaines choses que nous avons apprises au fil des années de travail, au cours des années d'essais et d'erreurs. Il existe de nombreuses approches pour structurer la base de code des projets. Mais parmi eux, il n'y a personne que l'on puisse appeler le seul juste.
La chose la plus importante que je voulais vous transmettre est que le programmeur doit s'efforcer de simplifier l'organisation des projets, leur homogénéité, d'utiliser une structure compréhensible et facile à travailler. Cela simplifie les projets de développement d'équipe.
Chers lecteurs! Que pensez-vous des idées de développement d'applications JavaScript à pile complÚte décrites dans cet article?

