11 marca 2025 (updated: 11 marca 2025)
Chapters
Duzi goście nie mylą się, gdy używają monorepo dla swoich projektów. Monorepo wraz z ogólnym szablonem mogą przyspieszyć konfigurację Twojego projektu (i obniżyć koszty). Dzielimy się naszym podejściem.
Wraz z ciągłym wzrostem złożoności aplikacji pojawia się problem - jak utrzymać wszystkie potrzeby i obniżyć koszty projektu, który wymaga tak wielu zależności od samego początku? W tym artykule omówimy sposób na zaoszczędzenie dni pracy zespołu przy rozpoczynaniu nowego projektu.
Nie ma jednego prostego sposobu, aby to zrobić, ale z latami doświadczeń i poszukiwaniem najlepszych praktyk z firm takich jak Google, w EL Passion opracowaliśmy rozwiązanie, które drastycznie skraca czas potrzebny na skonfigurowanie nowego projektu.
W tym artykule omówimy koncepcje szablonu projektu, monorepo oraz nasze własne podejście do tego tematu.
Istnieje powszechne przekonanie, że trzymanie całego kodu w jednym repozytorium wiąże się ze wszystkimi wadami aplikacji monolitycznych - nie skaluje się, zmusza nas do wydawania aplikacji razem… Jeśli tak, to dlaczego firmy takie jak Google czy Facebook używają monorepo? Prawda jest w rzeczywistości całkowicie odwrotna. Powód, dla którego podejście z jednym repozytorium na projekt jest tak popularne, to autonomia - zespoły mogą pracować w izolacji, podejmując decyzje oddzielnie. Chodzi o to, że przy odpowiednich narzędziach monorepo mogą mieć wszystkie zalety oddzielnych repozytoriów, a nawet więcej.
Oto niektóre z funkcji, które oferuje monorepo:
Używanie monorepo samo w sobie może obniżyć koszty rozwoju, ale poszliśmy o krok dalej i korzystając z monorepo, stworzyliśmy ogólny szablon dla naszych nadchodzących projektów.
Większość aplikacji zaczyna się w dość podobny sposób. Zawsze istnieje potrzeba skonfigurowania architektury i podstawowej konfiguracji aplikacji. Nawet bardziej specyficzne dla aplikacji moduły, takie jak autoryzacja, często mają wspólną logikę.
Mając to wszystko na uwadze, pojawia się pomysł stworzenia pewnego rodzaju szablonu, który można wykorzystać w różnych projektach, oszczędzając dni lub tygodnie pracy całego zespołu.
Aby udowodnić, że istnieje znacząca różnica w czasie między tworzeniem nowego projektu a korzystaniem z ogólnego, postanowiłem dać sobie 2 dni na skonfigurowanie projektu backendowego NestJS od podstaw.
Na początku skorzystałem z NestJS CLI i wygenerowałem projekt startowy, który zawierał kilka podstawowych konfiguracji. Następnie, mimo że pracuję z NestJS na co dzień, zajęło mi kilka godzin skonfigurowanie połączenia z bazą danych i prostej walidacji zmiennych środowiskowych. Po kolejnych kilku godzinach udało mi się uruchomić podstawowe testy i stworzyć pipeline CI w Github Actions, aby uruchamiać je automatycznie.
Zajęło mi trochę czasu, aby zdać sobie sprawę z popełnionych błędów, więc musiałem je naprawić. I oto byłem, po prawie 2 dniach zmagania się z podstawową konfiguracją aplikacji. Dlaczego tak się stało?
Konfiguracja Twojego projektu zazwyczaj odbywa się raz na kilka miesięcy lub nawet rzadziej. Niezależnie od tego, ile lat doświadczenia lub wiedzy można mieć, niemożliwe jest zapamiętanie wszystkich małych szczegółów, które są niezbędne do stworzenia funkcjonalnej aplikacji komercyjnej.
Nawet jeśli w jakiś sposób zapamiętałbym wszystko, co trzeba zrobić, zajęłoby to wieczność, aby doprowadzić to do etapu, w którym ma wszystkie funkcje i pomocniki, które my w EL Passion opracowaliśmy w naszym projekcie Flounder.
Jak już wspomniałem, większość konfiguracji projektów jest podobna - ale nie do końca taka sama. Powiedzmy, że mamy nasz projekt startowy monorepo gotowy… ale czy naprawdę? Mimo że przygotowaliśmy architekturę i podstawowe funkcje, nie jest on właściwie dostosowywalny. Nie chcemy przeszukiwać wszystkich plików i zmieniać wartości nazw, ścieżek itp. za każdym razem, gdy zaczynamy nowy projekt.
Aby to rozwiązać, opracowaliśmy rozwiązanie, które przekształca projekt w ogólny szablon, zastępując każdą wartość specyficzną dla projektu zmiennymi, aby można było go łatwo dostosować do różnych projektów.
Możesz się zastanawiać, dlaczego nie zaczynamy od zmiennych od samego początku. Ponieważ projekt Flounder jest ciągle aktualizowany, potrzebujemy, aby był w pełni funkcjonalny, aby deweloperzy mogli go uruchamiać i rozwijać jak normalny projekt.
Aby zautomatyzować procesy, stworzyliśmy pipeline CI, który najpierw uruchamia nasz skrypt, a następnie publikuje szablon w rejestrze pakietów Github, aby można go było wykonać za pomocą wiersza poleceń, zastępując wszystkie zmienne wartościami specyficznymi dla projektu lub nawet pomijając zasoby, których nie potrzebujemy w danym projekcie.
Stworzymy w pełni funkcjonalny szablon projektu z wszystkimi wymienionymi powyżej funkcjami. Zacznijmy od utworzenia pustego środowiska roboczego Nx monorepo za pomocą Terminala.
npx create-nx-workspace@latest
Powinieneś zobaczyć nowo utworzony projekt. Teraz chcemy zainstalować preset NestJS jako zależność deweloperską w naszym projekcie.
yarn add –dev @nrwl/nest
Gdy zostanie zainstalowany, możemy wygenerować nową aplikację.
nx generate @nrwl/nest:app backend
Stworzymy również aplikację node jako bibliotekę
yarn add –dev @nrwl/node
nx generate @nrwl/node:lib utils
Nx wygenerował całą konfigurację dla nas. Teraz możemy łatwo importować wszystko z biblioteki utils do każdego innego projektu. Poniżej możesz zobaczyć, jak zaimportowałem przykładową metodę z utils do usługi aplikacji w aplikacji NestJS.
Jak widać, nasza aplikacja działa doskonale i jest gotowa do rozwoju. Nx zajął się wszystkim za nas i możemy korzystać ze wszystkich zalet monorepo.
Uruchamianie projektu
Następnym krokiem jest uczynienie aplikacji ogólną, aby mogła być używana jako startowa dla każdego innego projektu. Stwórzmy folder “template-setup” w głównym katalogu naszego projektu oraz plik “revert-to-template.js” wewnątrz niego. Stworzymy również plik “config.js”, w którym przechowamy kilka pomocniczych wartości. Struktura powinna wyglądać następująco:
Struktura folderów projektu
Co chcemy teraz zrobić, to skopiować pliki projektu i zastąpić wartości, które chcemy, aby były konfigurowalne, zmiennymi EJS. Musimy również dodać prefiks “_” do wszystkich plików zawierających zmienne, aby mogły być oceniane jako pliki EJS.
Teraz, jeśli uruchomisz “node revert-to-template.js”, powinieneś zobaczyć nowo utworzony folder z zastąpionymi nazwami plików i naszą zmienną w package.json. Nie będziemy jednak uruchamiać tej komendy ręcznie, powinna być obsługiwana w przepływie pracy Github Actions, ale omówimy to później.
Z szablonem przygotowanym, napiszemy kod, który może go wykorzystać i wygenerować projekt. Zacznijmy od stworzenia prostego pliku package.json z dwoma pakietami i właściwością “bin”, abyśmy mogli go uruchomić jako plik wykonywalny. Upewnij się, że w właściwości “name” zawrzesz swój profil lub nazwę organizacji na GitHubie, ponieważ będzie to używane później w przepływie CI do publikacji kodu.
Na koniec skorzystamy z pakietu “commander”, aby stworzyć nasz program CLI. Będzie on przyjmował parametry podane przez użytkownika i generował nowy projekt przy użyciu pakietu “scaffe”.
Możemy teraz przetestować przepływ, uruchamiając pliki ręcznie. Najpierw uruchom “node revert-to-template.js”, tak jak wcześniej. Teraz zmień katalog na nowo wygenerowany szablon, zainstaluj pakiety (“yarn install”) i wpisz “node index.js ~/path-to-project -p ProjectName”. Powinieneś zobaczyć projekt z zastąpioną wartością właściwości “name” w pliku package.json.
Ostatnią rzeczą, którą musimy zrobić, jest stworzenie potoku CI, który publikuje nasz szablon do npm.
Jeśli nie jesteś zaznajomiony z tym, co dzieje się w tym fragmencie, zachęcam do przeczytania więcej na temat dokumentacji Github Actions. Zasadniczo użyłem akcji “setup-node”, która tworzy plik “.nprmc” (więcej tutaj: Publikowanie pakietów Node.js), uruchomiłem nasz skrypt revert-to-template i opublikowałem go, używając automatycznie wygenerowanego sekretu GITHUB_TOKEN. Dodatkowo użyłem akcji do automatycznego zwiększania wersji package.json, aby uniknąć konfliktów.
Teraz, jeśli wypchniemy nasz kod do repozytorium, powinniśmy zobaczyć działający przepływ pracy, a następnie nasz opublikowany pakiet.
Podsumowanie widoku przepływu pracy Github Actions
Spróbujmy go zainstalować i zobaczyć, czy wszystko działa! Pamiętaj, aby zalogować się do rejestru podczas instalacji prywatnych pakietów.
Ustawienie i generacja nowego szablonu projektu
I to wszystko! Powinieneś zobaczyć w pełni funkcjonalny wygenerowany projekt z zastąpioną wartością nazwy w package.json.
Znakiem dobrego procesu rozwoju oprogramowania jest stałe dążenie do uczynienia rzeczy bardziej elastycznymi i skalowalnymi, co bezpośrednio przekłada się na oszczędności kosztów i czasu. Przykładem jest nasz ogólny projekt szablonu.
Przykład z tego artykułu dotyczył tylko prostej wygenerowanej aplikacji z jedną zmienną, jaką jest nazwa aplikacji. W naszym wewnętrznym rozwiązaniu w EL Passion przygotowaliśmy wiele aplikacji (frontend i backend) z modułami do ponownego użycia, a nawet konfigurację terraform dla infrastruktury AWS, co oszczędza dni pracy wielu deweloperów.
Śledź nas, aby zobaczyć więcej treści!
11 marca 2025 • Maria Pradiuszyk
11 marca 2025 • Maria Pradiuszyk