11 mars 2025 (updated: 11 mars 2025)
Chapters
De store gutta tar ikke feil når de bruker monorepos for prosjektene sine. Monorepos sammen med en generell mal kan gjøre oppsettet av prosjektet ditt raskere (og billigere). Vi deler vår tilnærming.
Med den konstante veksten av kompleksitet i applikasjoner kommer et problem - hvordan opprettholde alle behovene og redusere kostnadene for et prosjekt som krever så mange avhengigheter fra starten av? I denne artikkelen vil vi dekke en måte å spare dager med teamets arbeid når man starter et nytt prosjekt.
Det finnes ingen enkel måte å gjøre det på, men med mange års erfaring og et søk etter beste praksis fra selskaper som Google, har vi i EL Passion utviklet en løsning som drastisk reduserer tiden som trengs for å sette opp et nytt prosjekt.
I denne artikkelen vil vi dekke konseptene til et malprosjekt, monorepos og vår egen tilnærming til dette emnet.
Det er en vanlig misforståelse at det å holde all kode i ett repository kommer med alle ulempene ved monolittiske applikasjoner - det skalerer ikke, det tvinger oss til å lansere apper sammen… Hvis ja, så hvorfor ville selskaper som Google eller Facebook bruke monorepos? Sannheten er faktisk ganske motsatt. Grunnen til at tilnærmingen med ett repository per prosjekt er så populær, er på grunn av autonomi - team kan jobbe i isolasjon, ta beslutninger separat. Saken er - med riktige verktøy kan monorepos ha alle fordelene til separate repositories og enda mer.
Her er noen av funksjonene monorepo tilbyr:
Å bruke monorepos alene kan redusere utviklingskostnadene, men vi gikk et skritt videre og utnyttet monorepos, vi laget en generell mal for våre kommende prosjekter.
Utviklingen av de fleste apper starter ganske likt. Det er alltid behov for å sette opp arkitekturen og grunnkonfigurasjonen av appen. Selv mer applikasjonsspesifikke moduler som autorisasjon har ofte noe felles logikk.
Med alt dette i tankene, kommer ideen om å lage en slags mal som kan gjenbrukes mellom forskjellige prosjekter, og spare dager eller uker med hele teamets arbeid.
For å bevise at det er en betydelig forskjell i tid mellom å opprette et nytt prosjekt og å bruke et generisk, bestemte jeg meg for å gi meg selv 2 dager til å sette opp et backend NestJS-prosjekt fra bunnen av.
Først utnyttet jeg NestJS CLI og genererte et startprosjekt som inkluderer noen grunnleggende konfigurasjoner. Deretter, selv om jeg jobber med NestJS daglig, tok det meg noen timer å sette opp databaseforbindelse og enkel validering av miljøvariabler. Etter et par timer til klarte jeg å kjøre grunnleggende tester og lage en Github Actions CI-pipeline for å kjøre dem automatisk.
Det tok meg litt tid å innse feilene jeg hadde gjort, så jeg måtte fikse dem. Og her var jeg, etter nesten 2 dager med å slite med å lage en grunnleggende appoppsett. Så hvorfor var det slik?
Oppsettet av prosjektet ditt gjøres vanligvis en gang hvert par måneder eller enda mer. Uansett hvor mange års erfaring eller kunnskap man måtte ha, er det umulig å huske alle de små detaljene som er nødvendige for å lage en funksjonell kommersiell applikasjon.
Selv om jeg på en eller annen måte husket alt som måtte gjøres, ville det tatt evigheter å komme til det stadiet hvor det har alle funksjonene og hjelperne som vi i EL Passion utviklet i vårt Flounder-prosjekt.
Som jeg nevnte før, er oppsettet av de fleste prosjekter likt - men ikke helt det samme. La oss si at vi har vårt startmonorepo-prosjekt klart… men har vi virkelig det? Selv om vi har forberedt en arkitektur og grunnleggende funksjoner, er det faktisk ikke tilpassbart. Vi ønsker ikke å lete gjennom alle filene og endre verdiene av navn, stier osv. hver gang vi starter et nytt prosjekt.
For å løse det utviklet vi en løsning som konverterer prosjektet til en generell mal ved å erstatte hver prosjektspecifik verdi med variabler, slik at det enkelt kan tilpasses for alle de forskjellige prosjektene.
Du lurer kanskje på hvorfor vi ikke begynner med variablene fra starten av. Siden Flounder-prosjektet kontinuerlig oppdateres, trenger vi at det er fullt funksjonelt, slik at utviklerne kan kjøre og utvikle det som et normalt prosjekt.
For å automatisere ting, laget vi en CI-pipeline som først kjører skriptet vårt og deretter publiserer malen til Github-pakkeregistret, slik at den kan kjøres via kommandolinjen, og erstatter alle variablene med prosjektspecifikke verdier eller til og med hopper over ressurser vi ikke trenger for det spesifikke prosjektet.
Vi skal lage et fullt funksjonelt malprosjekt med alle funksjonene nevnt ovenfor. La oss starte med å opprette et tomt Nx monorepo arbeidsområde ved hjelp av Terminal.
npx create-nx-workspace@latest
Du bør se et nyopprettet prosjekt. Nå ønsker vi å installere NestJS preset som en dev avhengighet i prosjektet vårt.
yarn add –dev @nrwl/nest
Når det er installert, kan vi generere en ny applikasjon.
nx generate @nrwl/nest:app backend
Vi vil også opprette en node-app som en lib
yarn add –dev @nrwl/node
nx generate @nrwl/node:lib utils
Nx har generert hele konfigurasjonen for oss. Vi kan nå enkelt importere alt fra utils-lib til hvert annet prosjekt. Nedenfor kan du se hvordan jeg importerte eksempelmetoden fra utils inn i app-tjenesten i NestJS-applikasjonen.
Som du kan se, fungerer appen vår helt fint, og den er klar for utvikling. Nx håndterte alt for oss, og vi kan dra nytte av alle fordelene med monorepos.
Kjører prosjektet
Neste del er å gjøre appen generisk, slik at den kan brukes som en starter for ethvert annet prosjekt. La oss opprette en “template-setup”-mappe i prosjektets hovedkatalog og en “revert-to-template.js”-fil inni den. Vi vil også opprette en “config.js”-fil hvor vi vil lagre noen hjelpeverdier. Strukturen bør se slik ut:
Prosjektets mappestruktur
Det vi ønsker å gjøre nå er å kopiere prosjektfilene og erstatte verdiene vi ønsker å gjøre tilpassbare med EJS-variabler. Vi må også legge til “_” prefiks til alle filer som inneholder variabler, slik at de kan evalueres som EJS-filer.
Nå, hvis du kjører “node revert-to-template.js”, bør du se en nyopprettet mappe med erstattede filnavn og vår variabel i package.json. Vi vil imidlertid ikke kjøre denne kommandoen manuelt, det bør håndteres i Github Actions arbeidsflyten, men vi vil dekke det senere.
Med malen forberedt, skal vi skrive en kode som kan bruke den og generere et prosjekt. La oss starte med å opprette en enkel package.json-fil med to pakker og en “bin” egenskap slik at vi kan kjøre den som en kjørbar. Sørg for å inkludere din github-profil eller organisasjonsnavn i “name” egenskapen, da det vil bli brukt senere i CI arbeidsflyten for å publisere koden.
Til slutt vil vi bruke “commander”-pakken for å lage vårt CLI-program. Det vil ta brukerens angitte parametere og generere et nytt prosjekt ved hjelp av “scaffe”-pakken.
Vi kan nå teste flyten ved å kjøre filene manuelt. Først, kjør “node revert-to-template.js” akkurat som før. Nå bytter du katalog til den nyopprettede malen, installerer pakkene (“yarn install”) og skriver “node index.js ~/path-to-project -p ProjectName”. Du bør se et prosjekt med den erstattede verdien av “name” egenskapen i package.json-filen.
Den siste tingen vi trenger å gjøre er å opprette en CI-pipeline som publiserer malen vår til npm.
Hvis du ikke er kjent med hva som skjer i denne koden, oppfordrer jeg deg til å lese mer om Github Actions-dokumentasjonen. I hovedsak brukte jeg en “setup-node”-handling som oppretter en “.nprmc”-fil (mer her: Publisering av Node.js-pakker), kjørte vår revert-to-template skript og publiserte det ved hjelp av den automatisk genererte GITHUB_TOKEN hemmeligheten. I tillegg brukte jeg en handling for automatisk å øke versjonen i package.json slik at det ikke oppstår konflikter.
Nå, hvis vi skyver koden vår til depotet, bør vi se en kjørende arbeidsflyt og deretter vår publiserte pakke.
Oversikt over Github Actions’ arbeidsflyt
La oss prøve å installere den og se om alt fungerer! Husk å logge inn på registeret når du installerer private pakker.
Oppsett og generering av et nytt malprosjekt
Og det er det! Du bør se et fullt funksjonelt generert prosjekt med erstattet package.json navnverdi.
Et tegn på en god programvareutviklingsprosess er den konstante jakten på å gjøre ting mer fleksible og skalerbare, noe som direkte oversettes til kostnads- og tidsbesparelser. Et av eksemplene er vårt generiske malprosjekt.
Eksemplet fra denne artikkelen inneholdt kun en enkel generert applikasjon med ett variabel som var appnavnet. I vår interne løsning hos EL Passion har vi forberedt flere applikasjoner (frontend og backend) med gjenbrukbare moduler og til og med en terraform-konfigurasjon for AWS-infrastruktur som sparer dager med arbeid for flere utviklere.
Hold deg oppdatert for mer innhold!
11 mars 2025 • Maria Pradiuszyk