Ich erstelle eine API für mehrere Kunden. Die wichtigsten Endpunkte /users
werden von jedem Kunden verwendet, einige Endpunkte sind jedoch auf individuelle Anpassungen angewiesen. Es kann also sein, dass Benutzer A einen speziellen Endpunkt wünscht /groups
und kein anderer Kunde über diese Funktion verfügt. Nur als Nebenbemerkung würde jeder Kunde aufgrund dieser zusätzlichen Funktionen auch sein eigenes Datenbankschema verwenden.
Ich persönlich benutze NestJs (Express unter der Haube). Das app.module
registriert also derzeit alle meine Kernmodule (mit eigenen Endpunkten etc.)
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module'; // core module
@Module({
imports: [UsersModule]
})
export class AppModule {}
Ich denke, dieses Problem hängt nicht mit NestJs zusammen. Wie würden Sie theoretisch damit umgehen?
Ich brauche grundsätzlich eine Infrastruktur, die ein Basissystem bereitstellen kann. Es gibt keine Kernendpunkte mehr, da jede Erweiterung eindeutig ist und mehrere /users
Implementierungen möglich sein könnten. Bei der Entwicklung einer neuen Funktion sollte die Kernanwendung nicht berührt werden. Erweiterungen sollten sich selbst integrieren oder beim Start integriert werden. Das Kernsystem wird ohne Endpunkte geliefert, wird jedoch von diesen externen Dateien erweitert.
Einige Ideen kommen mir in den Sinn
Erste Ansatz:
Jede Erweiterung repräsentiert ein neues Repository. Definieren Sie einen Pfad zu einem benutzerdefinierten externen Ordner, der alle Erweiterungsprojekte enthält. Dieses benutzerdefinierte Verzeichnis würde einen Ordner groups
mit einem enthaltengroups.module
import { Module } from '@nestjs/common';
import { GroupsController } from './groups.controller';
@Module({
controllers: [GroupsController],
})
export class GroupsModule {}
Meine API könnte dieses Verzeichnis durchlaufen und versuchen, jede Moduldatei zu importieren.
Profis:
- Der benutzerdefinierte Code wird vom Kern-Repository ferngehalten
Nachteile:
NestJs verwendet Typescript, daher muss ich zuerst den Code kompilieren. Wie würde ich den API-Build und die Builds aus den benutzerdefinierten Apps verwalten? (Plug and Play System)
Die benutzerdefinierten Erweiterungen sind sehr locker, da sie nur einige Typoskriptdateien enthalten. Aufgrund der Tatsache, dass sie keinen Zugriff auf das Verzeichnis node_modules der API haben, zeigt mir mein Editor Fehler an, da er externe Paketabhängigkeiten nicht auflösen kann.
Einige Erweiterungen rufen möglicherweise Daten von einer anderen Erweiterung ab. Möglicherweise muss der Gruppendienst auf den Benutzerdienst zugreifen. Hier könnte es schwierig werden.
Zweiter Ansatz: Bewahren Sie jede Erweiterung in einem Unterordner des src-Ordners der API auf. Fügen Sie diesen Unterordner jedoch der .gitignore-Datei hinzu. Jetzt können Sie Ihre Erweiterungen in der API behalten.
Profis:
Ihr Editor kann die Abhängigkeiten auflösen
Bevor Sie Ihren Code bereitstellen, können Sie den Build-Befehl ausführen und haben eine einzige Distribution
Sie können leicht auf andere Dienste zugreifen (
/groups
muss einen Benutzer anhand seiner ID finden)
Nachteile:
- Bei der Entwicklung müssen Sie Ihre Repository-Dateien in diesen Unterordner kopieren. Nachdem Sie etwas geändert haben, müssen Sie diese Dateien zurückkopieren und Ihre Repository-Dateien mit den aktualisierten überschreiben.
Dritter Ansatz:
In einem externen benutzerdefinierten Ordner sind alle Erweiterungen vollwertige eigenständige APIs. Ihre Haupt-API würde nur das Authentifizierungsmaterial bereitstellen und könnte als Proxy fungieren, um die eingehenden Anforderungen an die Ziel-API umzuleiten.
Profis:
- Neue Erweiterungen können einfach entwickelt und getestet werden
Nachteile:
Die Bereitstellung wird schwierig. Sie haben eine Haupt-API und n Erweiterungs-APIs, die ihren eigenen Prozess starten und einen Port abhören.
Das Proxy-System könnte schwierig sein. Wenn der Client anfordert, dass
/users
der Proxy wissen muss, welche Erweiterungs-API auf diesen Endpunkt wartet, ruft er diese API auf und leitet diese Antwort an den Client zurück.Um die Erweiterungs-APIs zu schützen (die Authentifizierung wird von der Haupt-API durchgeführt), muss der Proxy ein Geheimnis mit diesen APIs teilen. Daher leitet die Erweiterungs-API eingehende Anforderungen nur weiter, wenn dieses übereinstimmende Geheimnis vom Proxy bereitgestellt wird.
Vierter Ansatz:
Microservices könnten helfen. Ich habe einen Leitfaden von hier https://docs.nestjs.com/microservices/basics genommen
Ich könnte einen Microservice für die Benutzerverwaltung, Gruppenverwaltung usw. haben und diese Services nutzen, indem ich eine kleine API / ein Gateway / einen kleinen Proxy erstelle, die diese Microservices aufruft.
Profis:
Neue Erweiterungen können einfach entwickelt und getestet werden
Getrennte Bedenken
Nachteile:
Die Bereitstellung wird schwierig. Sie haben eine Haupt-API und n Microservices, die ihren eigenen Prozess starten und einen Port abhören.
Es scheint, dass ich für jeden Kunden eine neue Gateway-API erstellen müsste, wenn ich sie anpassbar haben möchte. Anstatt eine Anwendung zu erweitern, müsste ich jedes Mal eine angepasste Comsuming-API erstellen. Das würde das Problem nicht lösen.
Um die Erweiterungs-APIs zu schützen (die Authentifizierung wird von der Haupt-API durchgeführt), muss der Proxy ein Geheimnis mit diesen APIs teilen. Daher leitet die Erweiterungs-API eingehende Anforderungen nur weiter, wenn dieses übereinstimmende Geheimnis vom Proxy bereitgestellt wird.