Estou criando uma API para vários clientes. Os pontos finais principais /users
são usados por todos os clientes, mas alguns pontos finais dependem da personalização individual. Portanto, o usuário A deseja um terminal especial /groups
e nenhum outro cliente terá esse recurso. Assim como uma nota lateral , cada cliente também usaria seu próprio esquema de banco de dados por causa desses recursos extras.
Eu pessoalmente uso NestJs (Express sob o capô). Então, app.module
atualmente, registra todos os meus módulos principais (com seus próprios terminais etc.)
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module'; // core module
@Module({
imports: [UsersModule]
})
export class AppModule {}
Eu acho que esse problema não está relacionado aos NestJs, então como você lidaria com isso em teoria?
Basicamente, preciso de uma infraestrutura capaz de fornecer um sistema básico. Não há mais pontos de extremidade principais porque cada extensão é única e várias /users
implementações podem ser possíveis. Ao desenvolver um novo recurso, o aplicativo principal não deve ser tocado. As extensões devem se integrar ou se integrar na inicialização. O sistema principal é fornecido sem pontos de extremidade, mas será estendido desses arquivos externos.
Algumas idéias vêm à minha mente
Primeira abordagem:
Cada extensão representa um novo repositório. Defina um caminho para uma pasta externa personalizada contendo todos os projetos de extensão. Esse diretório personalizado conteria uma pasta groups
com umgroups.module
import { Module } from '@nestjs/common';
import { GroupsController } from './groups.controller';
@Module({
controllers: [GroupsController],
})
export class GroupsModule {}
Minha API pode percorrer esse diretório e tentar importar cada arquivo de módulo.
profissionais:
- O código customizado é mantido longe do repositório principal
contras:
Os NestJs usam o Typecript, então eu tenho que compilar o código primeiro. Como gerenciar a compilação da API e as compilações dos aplicativos personalizados? (Sistema Plug and Play)
As extensões personalizadas são muito frouxas porque contêm apenas alguns arquivos datilografados. Como eles não têm acesso ao diretório node_modules da API, meu editor me mostrará erros porque não pode resolver dependências externas de pacotes.
Algumas extensões podem buscar dados de outra extensão. Talvez o serviço de grupos precise acessar o serviço de usuários. As coisas podem ficar complicadas aqui.
Segunda abordagem: mantenha cada extensão dentro de uma subpasta da pasta src da API. Mas adicione essa subpasta ao arquivo .gitignore. Agora você pode manter suas extensões dentro da API.
profissionais:
Seu editor é capaz de resolver as dependências
Antes de implantar seu código, você pode executar o comando build e terá uma única distribuição
Você pode acessar outros serviços facilmente (
/groups
precisa encontrar um usuário por ID)
contras:
- Ao desenvolver, você deve copiar seus arquivos de repositório dentro dessa subpasta. Depois de alterar algo, você deve copiar esses arquivos novamente e substituir os arquivos do repositório pelos atualizados.
Terceira abordagem:
Dentro de uma pasta personalizada externa, todas as extensões são APIs autônomas. Sua API principal forneceria apenas o material de autenticação e poderia atuar como um proxy para redirecionar as solicitações recebidas para a API de destino.
profissionais:
- Novas extensões podem ser desenvolvidas e testadas facilmente
contras:
A implantação será complicada. Você terá um dos principais API e n de extensão APIs começando seu próprio processo e ouvir uma porta.
O sistema proxy pode ser complicado. Se o cliente solicitar que
/users
o proxy precise saber qual API de extensão atende esse ponto de extremidade, chama a API e encaminha a resposta de volta ao cliente.Para proteger as APIs de extensão (a autenticação é manipulada pela API principal), o proxy precisa compartilhar um segredo com essas APIs. Portanto, a API de extensão só passará solicitações de entrada se esse segredo correspondente for fornecido a partir do proxy.
Quarta abordagem:
Os microsserviços podem ajudar. Peguei um guia a partir daqui https://docs.nestjs.com/microservices/basics
Eu poderia ter um microsserviço para gerenciamento de usuários, gerenciamento de grupos etc. e consumir esses serviços criando uma pequena API / gateway / proxy que chama esses microsserviços.
profissionais:
Novas extensões podem ser desenvolvidas e testadas facilmente
Preocupações separadas
contras:
A implantação será complicada. Você terá uma API principal e n microservices começar seu próprio processo e ouvir uma porta.
Parece que eu precisaria criar uma nova API de gateway para cada cliente, se quiser personalizá-la. Então, em vez de estender um aplicativo, eu teria que criar uma API de consumo personalizada a cada vez. Isso não resolveria o problema.
Para proteger as APIs de extensão (a autenticação é manipulada pela API principal), o proxy precisa compartilhar um segredo com essas APIs. Portanto, a API de extensão só passará solicitações de entrada se esse segredo correspondente for fornecido a partir do proxy.
Respostas:
Existem várias abordagens para isso. O que você precisa fazer é descobrir qual fluxo de trabalho é mais adequado para sua equipe, organização e clientes.
Se isso dependesse de mim, consideraria usar um repositório por módulo e usar um gerenciador de pacotes como o NPM com pacotes com escopo privado ou organizacional para lidar com a configuração. Em seguida, configure os pipelines de liberação de compilação que são enviados para o repositório de pacotes em novas compilações.
Dessa forma, tudo o que você precisa é o arquivo principal e um arquivo de manifesto do pacote por instalação personalizada. Você pode desenvolver e implantar novas versões independentemente e carregar novas versões quando precisar no lado do cliente.
Para maior suavidade, você pode usar um arquivo de configuração para mapear módulos para rotas e escrever um script gerador de rotas genérico para executar a maior parte do processo de inicialização.
Como um pacote pode ser qualquer coisa, as dependências cruzadas dentro dos pacotes funcionarão sem muito aborrecimento. Você só precisa ser disciplinado quando se trata de gerenciamento de alterações e versões.
Leia mais sobre pacotes particulares aqui: Pacotes Particulares NPM
Agora, os registros privados do NPM custam dinheiro, mas se esse for um problema, também existem várias outras opções. Consulte este artigo para obter algumas alternativas - gratuitas e pagas.
Maneiras de ter seu registro npm privado
Agora, se você quiser rolar seu próprio gerente, poderá escrever um localizador de serviço simples, que inclua um arquivo de configuração que contenha as informações necessárias para extrair o código do repositório, carregá-lo e, em seguida, fornecer algum tipo de método para recuperar um instância para ele.
Eu escrevi uma implementação de referência simples para esse sistema:
A estrutura: localizador de serviços de locomoção
Um exemplo de plugin que verifica palíndromos: exemplo de plugin de locomoção
Um aplicativo usando a estrutura para localizar plugins: exemplo de aplicativo de locomoção
Você pode brincar com isso obtendo-o do npm usando.
npm install -s locomotion
Você precisará especificar umplugins.json
arquivo com o seguinte esquema:exemplo:
carregue-o assim: const loco = require ("locomotion");
Em seguida, ele retorna uma promessa que resolverá o objeto localizador de serviço, que possui o método localizador para obter seus serviços:
Observe que essa é apenas uma implementação de referência e não é robusta o suficiente para aplicações sérias. No entanto, o padrão ainda é válido e mostra a essência de escrever esse tipo de estrutura.
Agora, isso precisaria ser estendido com suporte para configuração de plugins, inicializações, verificação de erros, talvez adicionar suporte para injeção de dependência e assim por diante.
fonte
Eu iria para a opção de pacotes externos.
Você pode estruturar seu aplicativo para ter uma
packages
pasta. Eu teria compilações UMD compiladas de pacotes externos nessa pasta para que seu texto datilografado compilado não tenha nenhum problema com os pacotes. Todos os pacotes devem ter umindex.js
arquivo na pasta raiz de cada pacote.E seu aplicativo pode executar um loop pela pasta packages usando
fs
erequire
todos os pacotesindex.js
em seu aplicativo.Então, novamente, a instalação de dependência é algo que você precisa cuidar. Eu acho que um arquivo de configuração em cada pacote também pode resolver isso. Você pode ter um
npm
script personalizado no aplicativo principal para instalar todas as dependências do pacote antes de iniciar o aplicativo.Dessa forma, você pode adicionar novos pacotes ao seu aplicativo copiando e colando o pacote na pasta packages e reiniciando o aplicativo. Seus arquivos datilografados compilados não serão tocados e você não precisará usar o npm privado para seus próprios pacotes.
fonte
npm
. Acima está uma solução que você pode fazer para evitar uma conta npm privada. Além disso, acredito que você não precisa adicionar pacotes criados por alguém de fora da sua organização. Direita?npm
é esse o caminho ou qualquer outro gerenciador de pacotes. Nesses casos, minha solução não será suficiente.