Tworzę interfejs API dla wielu klientów. Podstawowe punkty końcowe, takie jak, /users
są używane przez każdego klienta, ale niektóre punkty końcowe zależą od indywidualnego dostosowania. Być może więc użytkownik A chce specjalnego punktu końcowego /groups
i żaden inny klient nie będzie miał tej funkcji. Podobnie jak sidenote , każdy klient używałby również własnego schematu bazy danych z powodu tych dodatkowych funkcji.
Osobiście używam NestJs (Express pod maską). Tak więc app.module
obecnie rejestruje wszystkie moje podstawowe moduły (z własnymi punktami końcowymi itp)
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module'; // core module
@Module({
imports: [UsersModule]
})
export class AppModule {}
Myślę, że ten problem nie jest związany z NestJ, więc jak poradziłbyś sobie z tym w teorii?
Zasadniczo potrzebuję infrastruktury, która jest w stanie zapewnić podstawowy system. Nie ma już podstawowych punktów końcowych, ponieważ każde rozszerzenie jest unikalne i /users
możliwe może być wiele implementacji. Podczas opracowywania nowej funkcji nie należy dotykać podstawowej aplikacji. Rozszerzenia powinny się integrować lub powinny zostać zintegrowane podczas uruchamiania. System podstawowy jest dostarczany bez punktów końcowych, ale zostanie rozszerzony z tych plików zewnętrznych.
Niektóre pomysły przychodzą mi na myśl
Pierwsze podejście:
Każde rozszerzenie reprezentuje nowe repozytorium. Zdefiniuj ścieżkę do niestandardowego folderu zewnętrznego zawierającego wszystkie projekty rozszerzeń. Ten niestandardowy katalog zawierałby folder groups
z plikiemgroups.module
import { Module } from '@nestjs/common';
import { GroupsController } from './groups.controller';
@Module({
controllers: [GroupsController],
})
export class GroupsModule {}
Mój interfejs API może przechodzić przez ten katalog i próbować importować każdy plik modułu.
plusy:
- Kod niestandardowy jest trzymany z dala od głównego repozytorium
Cons:
NestJs używa Typescript, więc najpierw muszę skompilować kod. Jak zarządzałbym kompilacją API i kompilacjami z niestandardowych aplikacji? (System plug and play)
Niestandardowe rozszerzenia są bardzo luźne, ponieważ zawierają tylko niektóre pliki maszynopisu. Ponieważ nie mają dostępu do katalogu node_modules API, mój edytor pokaże mi błędy, ponieważ nie może rozwiązać zależności zewnętrznych pakietów.
Niektóre rozszerzenia mogą pobierać dane z innego rozszerzenia. Może usługa grup musi mieć dostęp do usługi użytkowników. Tutaj może być trudniej.
Drugie podejście: przechowuj każde rozszerzenie w podfolderze folderu src interfejsu API. Ale dodaj ten podfolder do pliku .gitignore. Teraz możesz przechowywać rozszerzenia w interfejsie API.
plusy:
Twój edytor jest w stanie rozwiązać zależności
Przed wdrożeniem kodu możesz uruchomić polecenie kompilacji i mieć jedną dystrybucję
Możesz łatwo uzyskać dostęp do innych usług (
/groups
musi znaleźć użytkownika według identyfikatora)
Cons:
- Podczas programowania musisz skopiować pliki repozytorium w tym podfolderze. Po zmianie czegoś musisz skopiować te pliki z powrotem i zastąpić pliki repozytorium zaktualizowanymi plikami.
Trzecie podejście:
W zewnętrznym folderze niestandardowym wszystkie rozszerzenia są pełnoprawnymi samodzielnymi interfejsami API. Twój główny interfejs API po prostu zapewnia uwierzytelnianie i może działać jako serwer proxy, aby przekierowywać przychodzące żądania do docelowego interfejsu API.
plusy:
- Nowe rozszerzenia można łatwo opracowywać i testować
Cons:
Wdrożenie będzie trudne. Będziesz miał główny interfejs API oraz API rozszerzeń n, rozpoczynający własny proces i nasłuchujący na porcie.
System proxy może być trudny. Jeśli klient zażąda,
/users
aby serwer proxy wiedział, które rozszerzenie API nasłuchuje dla tego punktu końcowego, wywołuje ten interfejs API i przekazuje tę odpowiedź z powrotem do klienta.Aby chronić interfejsy API rozszerzeń (uwierzytelnianie jest obsługiwane przez główny interfejs API), serwer proxy musi udostępnić klucz tajny tym interfejsom API. Zatem interfejs API rozszerzenia będzie przekazywał przychodzące żądania tylko wtedy, gdy ten pasujący klucz tajny zostanie dostarczony z serwera proxy.
Czwarte podejście:
Mikrousługi mogą pomóc. Wziąłem stąd przewodnik https://docs.nestjs.com/microservices/basics
Mogę mieć mikrousługę do zarządzania użytkownikami, zarządzania grupami itp. I korzystać z tych usług, tworząc mały interfejs API / bramę / proxy, który wywołuje te mikrousługi.
plusy:
Nowe rozszerzenia można łatwo opracowywać i testować
Oddzielne obawy
Cons:
Wdrożenie będzie trudne. Trzeba będzie głównym API i n microservices rozpoczęciem własnego procesu i słuchać do portu.
Wygląda na to, że musiałbym utworzyć nowy interfejs API bramy dla każdego klienta, jeśli chcę mieć możliwość dostosowania. Zamiast rozszerzać aplikację, musiałbym za każdym razem tworzyć niestandardowy interfejs API do zbierania danych. To nie rozwiązałoby problemu.
Aby chronić interfejsy API rozszerzeń (uwierzytelnianie jest obsługiwane przez główny interfejs API), serwer proxy musi udostępnić klucz tajny tym interfejsom API. Zatem interfejs API rozszerzenia będzie przekazywał przychodzące żądania tylko wtedy, gdy ten pasujący klucz tajny zostanie dostarczony z serwera proxy.
Odpowiedzi:
Istnieje kilka podejść do tego. Musisz dowiedzieć się, jaki przepływ pracy najlepiej pasuje do Twojego zespołu, organizacji i klientów.
Gdyby to zależało ode mnie, rozważyłbym użycie jednego repozytorium na moduł i użycie menedżera pakietów, takiego jak NPM, z pakietami prywatnymi lub o zasięgu organizacji do obsługi konfiguracji. Następnie skonfiguruj potoki wydania kompilacji, które wypychają repozytorium pakietów na nowe kompilacje.
W ten sposób wystarczy plik główny i plik manifestu pakietu dla każdej instalacji niestandardowej. Możesz samodzielnie opracowywać i wdrażać nowe wersje oraz ładować nowe wersje, kiedy jest to konieczne po stronie klienta.
Aby zwiększyć płynność, można użyć pliku konfiguracyjnego do mapowania modułów na trasy i napisania ogólnego skryptu generatora tras, aby wykonać większość ładowania początkowego.
Ponieważ pakiet może być dowolny, wzajemne zależności w pakietach będą działać bez większych problemów. Musisz być zdyscyplinowany, jeśli chodzi o zarządzanie zmianami i wersjami.
Przeczytaj więcej o pakietach prywatnych tutaj: Pakiety prywatne NPM
Teraz prywatne rejestry NPM kosztują pieniądze, ale jeśli jest to problem, istnieje kilka innych opcji. Zapoznaj się z tym artykułem, aby uzyskać informacje na temat alternatyw - zarówno bezpłatnych, jak i płatnych.
Sposoby posiadania prywatnego rejestru npm
Teraz, jeśli chcesz uruchomić własnego menedżera, możesz napisać prosty lokalizator usług, który pobiera plik konfiguracyjny zawierający informacje niezbędne do pobrania kodu z repozytorium, załadowania go, a następnie udostępnienia metody pobierania instancja do tego.
Napisałem prostą referencyjną implementację dla takiego systemu:
Ramy: lokalizator usługi lokomocji
Przykładowa wtyczka sprawdzająca palindromy: przykład wtyczki locomotion
Aplikacja korzystająca z frameworka do lokalizowania wtyczek: przykład aplikacji locomotion
Możesz się tym pobawić, pobierając go z npm przy użyciu
npm install -s locomotion
, musisz określićplugins.json
plik o następującym schemacie:przykład:
załaduj go w ten sposób: const loco = wymagana („locomotion”);
Następnie zwraca obietnicę, która rozwiąże obiekt lokalizatora usług, który ma metodę lokalizatora, aby uzyskać kontrolę nad twoimi usługami:
Należy pamiętać, że jest to tylko implementacja referencyjna i nie jest wystarczająco odporna na poważne zastosowania. Jednak wzorzec jest nadal aktualny i pokazuje sedno pisania tego rodzaju frameworka.
Teraz trzeba by to rozszerzyć o obsługę konfiguracji wtyczek, inicjowanie, sprawdzanie błędów, może dodać obsługę wstrzykiwania zależności i tak dalej.
źródło
Wybrałbym opcję pakietów zewnętrznych.
Możesz zbudować aplikację tak, aby miała
packages
folder. Chciałbym, aby UMD skompilował kompilacje zewnętrznych pakietów w tym folderze, aby Twój skompilowany maszynopis nie miał żadnych problemów z pakietami. Wszystkie pakiety powinny miećindex.js
plik w folderze głównym każdego pakietu.A aplikacja może uruchomić pętlę poprzez pakiety folderu za pomocą
fs
irequire
wszystkich pakietówindex.js
do swojej aplikacji.Z drugiej strony należy zająć się instalacją zależności. Myślę, że plik konfiguracyjny na każdym pakiecie może to rozwiązać. Możesz mieć niestandardowy
npm
skrypt w głównej aplikacji, aby zainstalować wszystkie zależności pakietu przed uruchomieniem aplikacji.W ten sposób możesz po prostu dodać nowe pakiety do swojej aplikacji, kopiując wklejając pakiet do folderu paczek i ponownie uruchamiając aplikację. Twoje skompilowane pliki maszynopisu nie zostaną zmienione i nie musisz używać prywatnego npm dla własnych pakietów.
źródło
npm
. Powyżej znajduje się rozwiązanie, które możesz zrobić, aby uniknąć prywatnego konta npm. Ponadto uważam, że nie trzeba dodawać pakietów utworzonych przez osobę spoza organizacji. Dobrze?npm
jest to sposób na zrobienie tego lub dowolnego innego menedżera pakietów. W takich przypadkach moje rozwiązanie nie wystarczy.