Estoy creando una API para múltiples clientes. Los puntos finales centrales /users
son utilizados por cada cliente, pero algunos puntos finales dependen de la personalización individual. Por lo tanto, podría ser que el usuario A quiera un punto final especial /groups
y ningún otro cliente tendrá esa característica. Como nota al margen , cada cliente también usaría su propio esquema de base de datos debido a esas características adicionales.
Yo personalmente uso NestJs (Express debajo del capó). Entonces, app.module
actualmente registra todos mis módulos principales (con sus propios puntos finales, etc.)
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module'; // core module
@Module({
imports: [UsersModule]
})
export class AppModule {}
Creo que este problema no está relacionado con NestJs, entonces, ¿cómo manejarías eso en teoría?
Básicamente necesito una infraestructura que pueda proporcionar un sistema básico. Ya no hay puntos finales centrales porque cada extensión es única y /users
podrían ser posibles implementaciones múltiples . Al desarrollar una nueva característica, no se debe tocar la aplicación principal. Las extensiones deben integrarse o integrarse al inicio. El sistema central se envía sin puntos finales, pero se extenderá desde esos archivos externos.
Algunas ideas vienen a mi mente
Primer enfoque:
Cada extensión representa un nuevo repositorio. Defina una ruta a una carpeta externa personalizada que contenga todos esos proyectos de extensión. Este directorio personalizado contendría una carpeta groups
con ungroups.module
import { Module } from '@nestjs/common';
import { GroupsController } from './groups.controller';
@Module({
controllers: [GroupsController],
})
export class GroupsModule {}
Mi API podría recorrer ese directorio e intentar importar cada archivo de módulo.
pros:
- El código personalizado se mantiene alejado del repositorio central
contras:
NestJs usa Typecript, así que primero tengo que compilar el código. ¿Cómo gestionaría la compilación de API y las compilaciones de las aplicaciones personalizadas? (Sistema plug and play)
Las extensiones personalizadas son muy sueltas porque solo contienen algunos archivos de texto mecanografiado. Debido al hecho de que no tienen acceso al directorio node_modules de la API, mi editor me mostrará errores porque no puede resolver las dependencias de paquetes externos.
Algunas extensiones pueden obtener datos de otra extensión. Quizás el servicio de grupos necesita acceder al servicio de usuarios. Las cosas pueden ponerse difíciles aquí.
Segundo enfoque: mantenga cada extensión dentro de una subcarpeta de la carpeta src de la API. Pero agregue esta subcarpeta al archivo .gitignore. Ahora puede mantener sus extensiones dentro de la API.
pros:
Su editor puede resolver las dependencias.
Antes de implementar su código, puede ejecutar el comando de compilación y tendrá una única distribución
Puede acceder a otros servicios fácilmente (
/groups
necesita encontrar un usuario por id)
contras:
- Al desarrollar, debe copiar sus archivos de repositorio dentro de esa subcarpeta. Después de cambiar algo, debe volver a copiar estos archivos y anular los archivos del repositorio con los actualizados.
Tercer enfoque:
Dentro de una carpeta personalizada externa, todas las extensiones son API independientes completas. Su API principal solo proporcionaría las cosas de autenticación y podría actuar como un proxy para redirigir las solicitudes entrantes a la API de destino.
pros:
- Se pueden desarrollar y probar nuevas extensiones fácilmente
contras:
La implementación será complicada. Tendrá una API principal y n API de extensión que comenzarán su propio proceso y escucharán un puerto.
El sistema proxy podría ser complicado. Si el cliente solicita que
/users
el proxy necesite saber qué extensión API escucha ese punto final, llama a esa API y reenvía esa respuesta al cliente.Para proteger las API de extensión (la autenticación es manejada por la API principal), el proxy necesita compartir un secreto con esas API. Por lo tanto, la API de extensión solo pasará las solicitudes entrantes si ese proxy coincidente se proporciona desde el proxy.
Cuarto enfoque:
Los microservicios pueden ayudar. Tomé una guía de aquí https://docs.nestjs.com/microservices/basics
Podría tener un microservicio para la gestión de usuarios, la gestión de grupos, etc. y consumir esos servicios creando una pequeña api / gateway / proxy que llame a esos microservicios.
pros:
Se pueden desarrollar y probar nuevas extensiones fácilmente
Preocupaciones separadas
contras:
La implementación será complicada. Tendrá una API principal yn microservicios que comenzarán su propio proceso y escucharán un puerto.
Parece que tendría que crear una nueva API de puerta de enlace para cada cliente si quisiera personalizarla. Entonces, en lugar de extender una aplicación, tendría que crear una API de resumen personalizada cada vez. Eso no resolvería el problema.
Para proteger las API de extensión (la autenticación es manejada por la API principal), el proxy necesita compartir un secreto con esas API. Por lo tanto, la API de extensión solo pasará las solicitudes entrantes si ese proxy coincidente se proporciona desde el proxy.
Respuestas:
Hay varios enfoques para esto. Lo que debe hacer es determinar qué flujo de trabajo se adapta mejor a su equipo, organización y clientes.
Si esto dependiera de mí, consideraría usar un repositorio por módulo, y usaría un administrador de paquetes como NPM con paquetes privados o de organización para manejar la configuración. Luego configure las tuberías de lanzamiento de compilación que se envían al repositorio de paquetes en las nuevas compilaciones.
De esta manera, todo lo que necesita es el archivo principal y un archivo de manifiesto del paquete por instalación personalizada. Puede desarrollar e implementar nuevas versiones de forma independiente, y puede cargar nuevas versiones cuando lo necesite en el lado del cliente.
Para una mayor suavidad, puede usar un archivo de configuración para asignar módulos a rutas y escribir un script genérico de generador de ruta para realizar la mayor parte del arranque.
Como un paquete puede ser cualquier cosa, las dependencias cruzadas dentro de los paquetes funcionarán sin mucha molestia. Solo necesita ser disciplinado cuando se trata de cambios y gestión de versiones.
Lea más sobre paquetes privados aquí: Paquetes privados NPM
Ahora los registros privados de NPM cuestan dinero, pero si eso es un problema, también hay varias otras opciones. Revise este artículo para conocer algunas alternativas, tanto gratuitas como de pago.
Formas de tener su registro privado de npm
Ahora, si desea crear su propio administrador, puede escribir un localizador de servicios simple, que tome un archivo de configuración que contenga la información necesaria para extraer el código del repositorio, cargarlo y luego proporcionar algún tipo de método para recuperar un instancia a ello.
He escrito una implementación de referencia simple para dicho sistema:
El marco: localizador de servicios de locomoción
Un complemento de ejemplo que busca palíndromos: ejemplo de complemento de locomoción
Una aplicación que usa el marco para localizar complementos: ejemplo de aplicación de locomoción
Puede jugar con esto obteniéndolo de npm usando
npm install -s locomotion
tendrá que especificar unplugins.json
archivo con el siguiente esquema:ejemplo:
cargarlo así: const loco = require ("locomoción");
Luego devuelve una promesa que resolverá el objeto localizador de servicios, que tiene el método de localización para obtener sus servicios:
Tenga en cuenta que esta es solo una implementación de referencia y no es lo suficientemente robusta para una aplicación seria. Sin embargo, el patrón sigue siendo válido y muestra la esencia de escribir este tipo de marco.
Ahora, esto debería extenderse con soporte para configuración de complementos, inicializaciones, verificación de errores, tal vez agregar soporte para inyección de dependencia, etc.
fuente
Yo iría por la opción de paquetes externos.
Puede estructurar su aplicación para tener una
packages
carpeta. Tendría compilaciones compiladas de UMD de paquetes externos en esa carpeta para que su mecanografiado compilado no tenga ningún problema con los paquetes. Todos los paquetes deben tener unindex.js
archivo en la carpeta raíz de cada paquete.Y su aplicación puede ejecutar un bucle a través de la carpeta de paquetes usando
fs
yrequire
todos los paquetesindex.js
en su aplicación.Por otra parte, la instalación de dependencias es algo de lo que debe encargarse. Creo que un archivo de configuración en cada paquete también podría resolver eso. Puede tener un
npm
script personalizado en la aplicación principal para instalar todas las dependencias del paquete antes de iniciar la aplicación.De esta manera, puede agregar nuevos paquetes a su aplicación copiando y pegando el paquete en la carpeta de paquetes y reiniciando la aplicación. Sus archivos mecanografiados compilados no serán tocados y no tiene que usar npm privado para sus propios paquetes.
fuente
npm
. Lo anterior es una solución que puede hacer para evitar una cuenta privada npm. Además, creo que no necesita agregar paquetes creados por alguien fuera de su organización. ¿Derecha?npm
es la forma de hacerlo o de cualquier otro administrador de paquetes. En tales casos, mi solución no será suficiente.