Init.js : un guide sur le pourquoi et le comment du JavaScript Full-Stack
Publié: 2022-03-11L'histoire
Donc, vous et votre co-fondateur avez cette excellente idée pour une entreprise, n'est-ce pas ?
Vous avez ajouté des fonctionnalités dans votre esprit.
Fréquemment, vous demandez à des clients potentiels leur avis, et ils adorent tous ça.
Ok, donc les gens le veulent. Il y a même de l'argent à se faire. Et la seule raison pour laquelle ils ne peuvent pas l'avoir, c'est parce que vous ne l'avez pas encore implémenté.
Alors finalement, vous vous asseyez un jour et dites : « Faisons-le ! Bientôt, vous essayez de comprendre comment implémenter la logique métier de votre application, la fonctionnalité phare qui fera avancer le produit : vous avez une idée de la façon de le faire et vous savez que vous pouvez le faire.
"Terminé! Ça marche!" vous dites. Votre preuve de concept est un succès ! Il ne reste plus qu'à le regrouper dans une application Web.
"Ok, créons le site", dites-vous.
Et puis, vous réalisez la vérité : vous devez choisir un langage de programmation ; vous devez choisir une plate-forme (moderne) ; vous devez choisir des cadres (modernes) ; vous devez configurer (et acheter) du stockage, des bases de données et des fournisseurs d'hébergement ; vous avez besoin d'une interface d'administration ; vous avez besoin d'un système d'autorisations ; vous avez besoin d'un gestionnaire de contenu.
Vous avez des dizaines et des dizaines de décisions architecturales à prendre. Et vous voulez faire les bons : vous voulez utiliser des technologies qui permettent un développement rapide, une itération constante, une efficacité, une rapidité, une robustesse maximales, etc. Vous voulez être mince, vous voulez être agile. Vous souhaitez utiliser des technologies qui vous aideront à réussir à court et à long terme. Et ils ne sont pas toujours faciles à repérer.
« Je suis dépassé », dites-vous, alors que vous vous sentez dépassé. Votre énergie n'est plus la même qu'avant. Vous essayez de reconstituer les choses, mais c'est trop de travail.
Votre preuve de concept dépérit et meurt lentement.
La proposition
Après avoir moi-même abandonné des tonnes d'idées de cette manière, j'ai décidé de concevoir une solution. Je l'appelle le projet 'Init' (ou, init.js).
Le cœur de l'idée est d'avoir un seul projet pour tous les démarrer, de laisser le développeur ou le fondateur technique prendre toutes ces décisions essentielles en même temps, et de recevoir un modèle de départ approprié basé sur ces décisions. Je sais ce que les détracteurs vont dire : « Une solution ne peut pas s'appliquer à tous les problèmes » (les ennemis vont détester). Et ils ont peut-être raison. Mais nous pouvons faire de notre mieux pour créer une solution approximative, et je pense qu'Init est assez proche.
Pour atteindre au mieux cet objectif, nous devons garder à l'esprit quelques idées clés. Lors du développement d'Init, j'ai considéré :
Composants
La composition en composants est une caractéristique clé de tout système car elle vous permet de réutiliser des composants logiciels dans différents projets, ce qui est l'objectif principal d'Init. Mais la composantisation s'accompagne également d'un sous-produit, la "remplaçabilité", qui sera notre meilleur allié pour attaquer plusieurs problèmes différents avec "presque" la même solution.
Facilité de développement
Un problème, quelque part a une solution mieux écrite dans Brainf * ck. Mais la mise en œuvre de cette solution (dans Brainfuck) sera presque impossible à écrire, et encore moins à lire. Cela vous coûtera du temps et beaucoup d'efforts. En général, vous devez utiliser des langages et une plate-forme qui facilitent le développement, pas plus difficile pour vous (ou toute personne qui pourrait y travailler plus tard).
Communauté
Quelle que soit la plate-forme que vous choisissez, assurez-vous qu'elle dispose d'une grande communauté et qu'elle peut vous aider à résoudre les problèmes les plus courants et les moins courants. N'oubliez pas : jQuery n'est peut-être pas la bibliothèque la plus rapide, la plus propre ou la plus élégante, mais elle est gagnante uniquement grâce à sa communauté.
En gardant ces objectifs à l'esprit, je vais maintenant vous montrer comment j'ai pris mes propres décisions lors de la création d'Init.
À la base, Init tire parti du paradigme "JavaScript à pile complète" (certains l'appellent, ou un sous-ensemble de celui-ci, la pile MEAN). En travaillant avec une telle pile, Init est capable d'utiliser un seul langage tout en créant un environnement incroyablement flexible et complet pour le développement d'applications Web. En bref, Init vous permet d'utiliser JavaScript non seulement pour le développement client et serveur, mais également pour la construction, les tests, la création de modèles, etc.
Mais ralentissons un instant et demandons-nous : est-ce vraiment une bonne idée d'utiliser JavaScript ?
Pourquoi j'ai choisi JavaScript
Je suis développeur Web depuis 1998. À l'époque, nous utilisions Perl pour la plupart de nos développements côté serveur, mais même depuis lors, nous avons eu JavaScript côté client. Les technologies de serveur Web ont énormément changé depuis : nous avons traversé des vagues successives de langages et de technologies tels que PHP, AP, JSP, .NET, Ruby, Python, pour n'en nommer que quelques-uns. Les développeurs ont commencé à réaliser que l'utilisation de deux langages différents pour les environnements client et serveur compliquait les choses. Les premières tentatives d'unification sous un seul langage ont tenté de créer des composants clients sur le serveur et de les compiler en JavaScript. Cela n'a pas fonctionné comme prévu et la plupart de ces projets ont échoué (par exemple : ASP MVC remplaçant les formulaires Web ASP.NET et GWT probablement remplacé dans un proche avenir par Polymer). Mais c'était une idée géniale, en substance : un seul langage sur le client et le serveur, nous permettant de réutiliser les composants et les ressources (c'est le mot-clé : resources ).
La réponse était simple : placez JavaScript sur le serveur.
JavaScript est né avec JavaScript Server Side dans Netscape Enterprise Server, mais le langage n'était tout simplement pas prêt à l'époque. Après des années d'essais et d'erreurs, Node.js a finalement émergé, ce qui a non seulement mis JavaScript sur le serveur, mais a également promu l'idée d'une programmation non bloquante, changeant à jamais la façon dont nous écrivons un "fread" (I/O) (lire ici pour plus).
Mais ces idées n'étaient pas nouvelles, alors pourquoi sont-elles devenues si populaires avec Node.js ? Une programmation simple et non bloquante peut être réalisée de plusieurs manières. Le plus simple est peut-être d'utiliser des rappels et une boucle d'événements. Dans la plupart des langages, ce n'est pas une tâche facile : alors que les "rappels" sont une fonctionnalité courante dans certains autres langages, une boucle d'événement ne l'est pas, et vous vous retrouvez souvent aux prises avec des bibliothèques externes (par exemple : Python, avec Tornado). Mais en JavaScript, les callbacks sont intégrés au langage, tout comme la boucle d'événements, et presque tous les programmeurs qui se sont même essayés à JavaScript les connaissent (ou les ont au moins utilisés, même s'ils ne comprennent pas très bien ce que l'événement boucle est). Soudainement, chaque startup sur Terre pourrait réutiliser les développeurs (c'est-à-dire les ressources) côté client et côté serveur, résolvant ainsi le problème de l'offre d'emploi "Python Guru Needed".
Nous avons donc maintenant une plate-forme incroyablement rapide (grâce à la programmation non bloquante) avec un langage de programmation incroyablement facile à utiliser (grâce à JavaScript). Mais est-ce suffisant ? Est-ce que ça va durer ? Je suis sûr que JavaScript aura une place importante dans le futur. Laissez-moi vous dire pourquoi:
Programmation fonctionnelle
JavaScript a été le premier langage de programmation à apporter le paradigme fonctionnel aux masses (bien sûr, Lisp est venu en premier, mais la plupart des programmeurs n'ont jamais construit d'application prête pour la production en utilisant Lisp). Lisp et Self, les principales influences de Javascript, regorgent d'idées innovantes. Ces idées pourraient libérer nos esprits pour explorer de nouvelles techniques, modèles et paradigmes. Et ils se répercutent tous sur JavaScript. Jetez un œil aux monades, aux numéros d'église ou même (pour un exemple plus pratique) aux fonctions de collections d'Underscore.js, qui peuvent vous faire économiser des lignes et des lignes de code.
Objets dynamiques et héritage prototype
La programmation orientée objet sans classes (et sans hiérarchies infinies de classes) permet un développement rapide (créer des objets, ajouter des méthodes et les utiliser) mais, surtout, réduit le temps de refactorisation pendant les tâches de maintenance en permettant au programmeur de modifier les instances d'objets à la place de cours. Cette rapidité et cette flexibilité ouvrent la voie à un développement rapide.
JavaScript est Internet
JavaScript a été conçu pour Internet, il est là depuis le début et il ne va pas disparaître. Toutes les tentatives pour le détruire ont échoué : voyez, par exemple, la chute des applets Java, le remplacement de VBScript par TypeScript de Microsoft (qui compile en JavaScript) et la disparition de Flash aux mains du marché mobile et de HTML5. Il est impossible de remplacer Javascript sans casser des millions de pages Web, donc nos objectifs à l'avenir devraient être de l'améliorer. Et il n'y a personne de meilleur pour le travail que le comité technique 39 de l'ECMA.
Ok, des alternatives à JavaScript naissent tous les jours, comme CoffeeScript, TypeScript et les millions de langages qui se compilent en JavaScript. Ces alternatives pourraient être utiles pour les étapes de développement (via les cartes sources), mais elles ne parviendront pas à supplanter JavaScript à long terme pour deux raisons : leurs communautés ne seront jamais plus grandes et leurs meilleures fonctionnalités seront adoptées par le script ECMA (c'est-à-dire, JavaScript ). JavaScript n'est pas un langage d'assemblage : c'est un langage de programmation de haut niveau avec un code source que vous pouvez comprendre, vous devez donc le comprendre.
JavaScript de bout en bout : Node.js et MongoDB
Donc, ce sont les raisons d'utiliser JavaScript. Maintenant, j'utiliserai JavaScript comme raison d'utiliser Node.js et MongoDB.
Node.js
Node.js est une plate-forme permettant de créer des applications réseau rapides et évolutives, c'est à peu près ce que dit le site Node.js. Mais Node.js est plus que cela : c'est l'environnement d'exécution préféré pour toute application JavaScript avec accès E/S. Même si vous ne prévoyez pas d'écrire votre application serveur principale avec Node.js, vous pouvez utiliser des outils construits sur Node.js pour améliorer votre processus de développement. Par exemple : Mocha.js pour les tests unitaires, Grunt.js pour les tâches de construction automatisées ou même Brackets pour l'édition de code en texte intégral.
Donc, si vous allez écrire des applications JavaScript pour le serveur ou le client, vous devriez jeter un œil à quelques exemples Node.js, car vous en aurez besoin et l'utiliserez quotidiennement. Il existe des alternatives intéressantes, mais aucune d'entre elles ne représente même 10% de la communauté Node.js.
MongoDB
MongoDB est une base de données basée sur des documents NoSQL qui utilise JavaScript comme langage de requête, ce qui me permet de compléter la plateforme JavaScript de bout en bout. Mais ce n'est même pas la principale raison de choisir cette base de données.
MongoDB est une base de données sans schéma qui vous permet de conserver vos objets de manière flexible et ainsi de vous adapter plus rapidement aux changements d'exigences. De plus, il est hautement évolutif et basé sur la réduction de carte, ce qui le rend adapté aux applications Big Data. MongoDB est si flexible qu'il peut être utilisé comme une base de données de documents sans schéma, un magasin de données relationnel (bien qu'il manque de transactions) ou même comme un magasin clé-valeur pour la mise en cache des réponses.
Composantisation du serveur avec Express.js
La création de composants côté serveur n'est jamais facile. Mais avec Express.js (et Connect.js) est venue l'idée de « middleware ». À mon avis, le middleware est le meilleur moyen de définir des composants sur le serveur. Si vous voulez le comparer à un modèle connu, il est assez proche des tuyaux et des filtres.
L'idée de base est que votre composant fait partie d'un pipeline. Le pipeline traite une demande (entrée) et génère une réponse (sortie), mais votre composant n'est pas responsable de l'intégralité de la réponse. Au lieu de cela, il ne modifie que ce dont il a besoin, puis délègue au prochain élément du pipeline. Lorsque le dernier élément du pipeline est terminé, la réponse est renvoyée au client.
Nous appelons ces « éléments du pipeline » des « intergiciels ». En clair, nous pouvons créer deux types de middleware :
Intermédiaires : Ceux qui traitent la requête et la réponse, mais ne sont pas entièrement responsables de la réponse elle-même, ils délèguent donc au middleware suivant.
Finales : Ceux qui ont l'entière responsabilité de la réponse finale. Ils traitent et modifient la requête et la réponse, mais n'ont pas besoin de déléguer au middleware suivant. En pratique, il est recommandé de déléguer à un middleware suivant de toute façon pour permettre une flexibilité architecturale (c'est-à-dire ajouter plus de middleware plus tard), même si ce middleware n'existe pas (auquel cas la réponse ira directement au client).
A titre d'exemple concret, considérons un composant 'gestionnaire d'utilisateurs' sur le serveur. En termes de middleware, nous aurions à la fois des finales et des intermédiaires. Pour nos finales, nous aurions des fonctionnalités telles que la création d'un utilisateur et la liste des utilisateurs. Mais avant de pouvoir effectuer ces actions, nous avons besoin de nos intermédiaires pour l'authentification (car nous ne voulons pas que des requêtes non authentifiées arrivent et créent des utilisateurs). Une fois que nous avons créé ces intermédiaires d'authentification, nous pouvons simplement les brancher partout où nous voulons transformer une fonctionnalité précédemment non authentifiée en une fonctionnalité authentifiée.
Demandes d'une seule page
Le projet Init se concentre sur la création d'applications monopage (SPA). La plupart des développeurs Web ont été tentés plus d'une fois de s'essayer aux SPA. J'en ai construit plusieurs (principalement propriétaires) et je peux dire avec confiance qu'elles sont tout simplement l'avenir des applications Web. Avez-vous déjà comparé un SPA à une application Web ordinaire sur une connexion mobile ? La différence de réactivité est de l'ordre de quelques dizaines de secondes.
Les SPA sont l'avenir du Web, alors pourquoi créeriez-vous votre produit sous une forme héritée ? Un argument courant que j'entends est que les gens s'inquiètent du référencement. Mais si vous gérez les choses correctement, cela ne devrait pas être un problème : Google lui-même a un très bon tutoriel sur la façon de le faire, et il y a aussi de bons commentaires ici.
MV côté client* avec Backbone.js, Marionette.js et Twitter Bootstrap
On a beaucoup parlé des frameworks MVC* pour les SPA. C'est un choix difficile, mais je dirais que les trois premiers sont Backbone.js, Ember.js et Angular.js.
Tous les trois sont très appréciés. Mais lequel vous convient le mieux ?
Malheureusement, je dois admettre que j'ai une expérience très limitée avec Angular.js, donc je vais le laisser en dehors de cette discussion (pour en savoir plus, consultez le tutoriel Angular.js). Maintenant, Ember.js et Backbone.js représentent deux manières différentes d'attaquer le même problème.
Backbone.js est minimal, simpliste et vous offre juste ce qu'il faut pour créer un SPA simple. Ember.js, quant à lui, est un framework complet et professionnel pour la création de SPA. Il a plus de cloches et de sifflets, mais aussi une plus grande courbe d'apprentissage.
En fonction de la taille de votre application, la décision peut être aussi simple que de regarder le rapport caractéristiquesutilisées/fonctionnalitésdisponibles, ce qui vous donnera un indice important.
Dans le cas d'Init, je voulais couvrir la plupart des scénarios, j'ai donc choisi Backbone.js pour la création facile de SPA, avec Backbone.Marionette.View pour la composition. De cette façon, chaque composant est une application simple et l'application finale peut être aussi complexe que nous le souhaitons.
Le style est également un défi, mais nous pouvons à nouveau compter sur des cadres pour nous renflouer. Pour CSS, il n'y a pas mieux que Twitter Bootstrap, qui offre un ensemble complet de styles prêts à l'emploi et faciles à personnaliser.
Bootstrap a été créé en utilisant le langage LESS, et il est open source, nous pouvons donc le modifier si nécessaire. Il est livré avec une tonne de contrôles UX qui sont bien documentés sur le site Bootstrap. De plus, il existe un modèle de personnalisation qui vous permet de créer le vôtre. C'est définitivement l'homme de la situation.
Meilleures pratiques : Grunt.js, Mocha.js, Chai.js, RequireJS et CoverJS
Enfin, nous devrions définir certaines de nos meilleures pratiques et examiner comment Init peut vous aider à les mettre en œuvre et à les maintenir. Notre solution est centrée sur plusieurs outils, qui sont basés sur Node.js eux-mêmes.
Mocha.js et Chai.js :
Ces outils vous permettent d'améliorer votre processus de développement en appliquant TDD ou BDD, en fournissant l'infrastructure pour organiser vos tests unitaires et un exécuteur pour les exécuter automatiquement.
Il existe des milliers de frameworks de tests unitaires pour JavaScript. Alors pourquoi utiliser Mocha.js ? La réponse courte : c'est flexible et complet.
La réponse longue : il a deux caractéristiques importantes (interfaces, reporters) et une absence significative (affirmations). Laisse-moi expliquer.
Interfaces : peut-être êtes-vous habitué aux concepts TDD de suites et de tests unitaires, ou peut-être préférez-vous les idées BDD de spécifications de comportement avec « describe » et « it should ». Mocha.js vous permet d'utiliser les deux approches.
Reporters : l'exécution de votre test générera des rapports sur les résultats, et vous pouvez formater ces résultats à l'aide de divers reporters. Par exemple, si vous avez besoin d'alimenter un serveur d'intégration continue, vous pouvez trouver un journaliste pour le faire.
Absence de bibliothèque d'assertions : loin d'être un problème, Mocha.js a été conçu pour vous permettre d'utiliser la bibliothèque d'assertions de votre choix, vous donnant encore plus de flexibilité. Il existe de nombreuses options, mais c'est là que Chai.js entre en jeu.
Chai.js est une bibliothèque d'assertion flexible qui vous permet d'utiliser l'un des trois principaux styles d'assertion :
Assert : Style d'affirmation classique de la vieille école TDD. Par exemple:
assert.equal(variable, ”value”);
Attendez -vous à : style d'assertion chaînée, le plus couramment utilisé dans BDD. Par exemple:
expect(variable).to.equal(“value”);
Should : Egalement utilisé dans BDD, mais je préfère Expect parce que Should semble répétitif avec la spécification de comportement 'it ("devrait faire quelque chose..")'. Par exemple:
variable.should.equal(“value”);
Chai.js se combine parfaitement avec Mocha.js. En utilisant uniquement ces deux bibliothèques, vous pouvez écrire vos tests en TDD, BDD ou dans n'importe quel style imaginable.
Grunt.js :
Grunt.js vous permet d'automatiser les tâches de construction, allant du simple copier-coller et de la concaténation de fichiers, à la pré-compilation de modèles, à la compilation du langage de style (c'est-à-dire SASS et LESS), aux tests unitaires (avec mocha.js), au linting et minification du code (par exemple, avec UglifyJS ou Closure Compiler). Vous pouvez ajouter votre propre tâche automatisée à Grunt ou rechercher dans le registre Grunt, où des centaines et des centaines de plugins sont disponibles (encore une fois, l'utilisation d'outils avec de grandes communautés derrière eux est payante). Grunt peut également surveiller vos fichiers et déclencher des actions lorsqu'ils sont modifiés.
RequireJS :
RequireJS peut sembler être une autre façon de charger des modules avec AMD, mais je peux vous assurer que c'est bien plus que cela. Pour comprendre pourquoi, nous devons d'abord mentionner l'idée de l'espacement des noms de modules (par exemple, demo.views.hello), qui évite de polluer l'espace de noms global en enveloppant chaque module dans son propre espace de noms. Le problème est que ces modules ne sont pas réutilisables : si vous modifiez l'espace de noms d'une 'instance', vous modifiez l'espace de noms de toutes les 'instances'. Contrairement à cela, RequireJS vous permet de définir des modules réutilisables dès le départ. (De plus, cela vous aidera à adopter Dependency Injection pour éviter que vos modules n'accèdent à des variables globales.)
CoverJS :
La couverture de code est une métrique pour évaluer vos tests. Comme son nom l'indique, il vous indique quelle partie de votre code est couverte par votre suite de tests actuelle. CoverJS mesure la couverture de code de vos tests en instrumentant des instructions (au lieu de lignes de code comme JSCoverage) dans votre code et en générant une version instrumentée de votre code. Il peut également générer des rapports pour alimenter votre serveur d'intégration continue.
Utilisation de branches pour basculer les fonctionnalités
Lorsque j'ai lancé Init, j'avais besoin d'un moyen pour les utilisateurs d'activer et de désactiver diverses fonctionnalités qu'ils pourraient souhaiter dans leur projet. J'ai décidé d'adopter une approche radicale du système de branche de git pour implémenter cette fonctionnalité.
Essentiellement, chaque branche représente une caractéristique ou une fonctionnalité qu'un utilisateur peut vouloir inclure. Si vous démarrez un projet à partir de zéro, commencez par la branche minimale dont vous avez besoin, puis ajoutez d'autres technologies en fusionnant avec les branches souhaitées. Par exemple, supposons que vous souhaitiez démarrer votre projet avec Backbone.js et Marionette.js. Eh bien, vous pouvez commencer sur la branche Backbone.js et la fusionner avec la branche Marionette, en continuant pour chaque fonctionnalité que vous souhaitez ajouter.
Pour l'instant, cette idée de fusionner pour ajouter des fonctionnalités ne peut être utilisée que pour les modèles technologiques (par exemple, Backbone, Node, Express). Mais à l'avenir, vous pourrez basculer entre les implémentations back-end (par exemple, de MongoDB à Postgres) et les implémentations client.
Démarrez un projet avec Init et déployez-le sur Heroku dès aujourd'hui
Il n'y a jamais eu de moyen plus simple de démarrer un projet. Dirigez-vous simplement vers le référentiel GitHub, recherchez la branche avec les derniers commits (pour le moment, il s'agit de usermanager, bien que cela puisse changer à l'avenir), puis :
- Créez le répertoire de votre projet (ou utilisez-en un existant).
- Créez votre référentiel avec "git init" (ou utilisez le référentiel existant).
Ajouter une télécommande avec init
git remote add init git://github.com/picanteverde/init.git
Obtenez la branche que vous voulez
git pull init usermanager
Obtenir le fichier de processus Heroku
git pull init heroku-webprocess
Avec la ceinture à outils Heroku installée, créez une application Heroku
heroku create
Poussez votre branche principale vers Heroku
git push heroku master
- Visitez votre application, opérationnelle sur Heroku !
Vous pouvez maintenant commencer à développer votre fonctionnalité phare avec seulement quelques lignes de code. De plus, vous développerez avec les technologies les plus récentes et les plus efficaces dans une suite de développement aussi automatisée que possible.
J'espère que vous pourrez utiliser Init pour lancer votre prochaine grande idée. N'oubliez pas de consulter le référentiel Init pour les nouveaux correctifs et fonctionnalités - son développement est très actif et j'ai hâte d'entendre vos commentaires.