Um kit de ferramentas Node.js para arquiteturas de microsserviços
Este módulo de código aberto é patrocinado e apoiado pela Voxgig. |
---|
Seneca é um kit de ferramentas para escrever microsserviços e organizar a lógica de negócios do seu aplicativo. Você pode dividir seu aplicativo em “coisas que acontecem”, em vez de focar em modelos de dados ou gerenciar dependências.
Sêneca fornece,
correspondência de padrões: uma maneira maravilhosamente flexível de lidar com requisitos de negócios
independência de transporte: como as mensagens chegam ao servidor certo não é algo com que você deva se preocupar
maturidade: 8 anos em produção (antes de chamá-lo de microsserviços ), mas já foi eliminado por um raio
mais: um amplo e profundo ecossistema de plug-ins
livro: um guia para projetar arquiteturas de microsserviços: taomicro
Use este módulo para definir comandos que funcionam recebendo algum JSON e, opcionalmente, retornando algum JSON. O comando a ser executado é selecionado pela correspondência de padrões no JSON de entrada. Existem conjuntos de comandos integrados e opcionais que ajudam você a construir produtos mínimos viáveis: armazenamento de dados, gerenciamento de usuários, lógica distribuída, cache, registro, etc. E você pode definir seu próprio produto dividindo-o em um conjunto de comandos - " coisas que acontecem". É basicamente isso.
Se estiver usando este módulo e precisar de ajuda, você pode:
Se você é novo no Seneca em geral, dê uma olhada em senecajs.org. Temos tudo, desde tutoriais até aplicativos de exemplo, para ajudar você a começar a trabalhar rapidamente.
A fonte do Seneca pode ser lida de forma anotada executando npm run annotate
. Uma versão anotada de cada arquivo será gerada em ./docs/
.
Para instalar via npm,
npm install seneca
'use strict'
var Seneca = require ( 'seneca' )
// Functionality in seneca is composed into simple
// plugins that can be loaded into seneca instances.
function rejector ( ) {
this . add ( 'cmd:run' , ( msg , done ) => {
return done ( null , { tag : 'rejector' } )
} )
}
function approver ( ) {
this . add ( 'cmd:run' , ( msg , done ) => {
return done ( null , { tag : 'approver' } )
} )
}
function local ( ) {
this . add ( 'cmd:run' , function ( msg , done ) {
this . prior ( msg , ( err , reply ) => {
return done ( null , { tag : reply ? reply . tag : 'local' } )
} )
} )
}
// Services can listen for messages using a variety of
// transports. In process and http are included by default.
Seneca ( )
. use ( approver )
. listen ( { type : 'http' , port : '8260' , pin : 'cmd:*' } )
Seneca ( )
. use ( rejector )
. listen ( 8270 )
// Load order is important, messages can be routed
// to other services or handled locally. Pins are
// basically filters over messages
function handler ( err , reply ) {
console . log ( err , reply )
}
Seneca ( )
. use ( local )
. act ( 'cmd:run' , handler )
Seneca ( )
. client ( { port : 8270 , pin : 'cmd:run' } )
. client ( { port : 8260 , pin : 'cmd:run' } )
. use ( local )
. act ( 'cmd:run' , handler )
Seneca ( )
. client ( { port : 8260 , pin : 'cmd:run' } )
. client ( { port : 8270 , pin : 'cmd:run' } )
. use ( local )
. act ( 'cmd:run' , handler )
// Output
// null { tag: 'local' }
// null { tag: 'approver' }
// null { tag: 'rejector' }
Para executar normalmente, digamos em um contêiner, use
$ node microservice.js
(onde microservice.js
é um arquivo de script que usa Seneca). Os logs são gerados no formato JSON para que você possa enviá-los para um serviço de log.
Para executar no modo de teste, com logs de depuração completos e legíveis, use:
$ node microservice.js --seneca.test
Para que isso não importe,
Contanto que algum comando possa lidar com um determinado documento JSON, você está bem.
Aqui está um exemplo:
var seneca = require ( 'seneca' ) ( )
seneca . add ( { cmd : 'salestax' } , function ( msg , done ) {
var rate = 0.23
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
seneca . act ( { cmd : 'salestax' , net : 100 } , function ( err , result ) {
console . log ( result . total )
} )
Neste código, sempre que seneca vê o padrão {cmd:'salestax'}
, ele executa a função associada a este padrão, que calcula o imposto sobre vendas. Não há nada de especial na propriedade cmd
. É simplesmente a propriedade que queremos combinar com o padrão. Você poderia procurar foo
para todos os cuidados do seneca! Sim!
O método seneca.add
adiciona um novo padrão e a função a ser executada sempre que esse padrão ocorrer.
O método seneca.act
aceita um objeto e executa o comando correspondente, se houver.
De onde vem a taxa de imposto sobre vendas? Vamos tentar novamente:
seneca . add ( { cmd : 'config' } , function ( msg , done ) {
var config = { rate : 0.23 }
var value = config [ msg . prop ]
done ( null , { value : value } )
} )
seneca . add ( { cmd : 'salestax' } , function ( msg , done ) {
seneca . act ( { cmd : 'config' , prop : 'rate' } , function ( err , result ) {
var rate = parseFloat ( result . value )
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
} )
seneca . act ( { cmd : 'salestax' , net : 100 } , function ( err , result ) {
console . log ( result . total )
} )
O comando config
fornece sua configuração. Isso é legal porque não importa de onde a configuração é obtida - codificada, sistema de arquivos, banco de dados, serviço de rede, qualquer que seja. Você teve que definir uma API de abstração para fazer isso funcionar? Não.
Há um pouco, mas muita verbosidade aqui, você não acha? Vamos consertar isso:
seneca . act ( 'cmd:salestax,net:100' , function ( err , result ) {
console . log ( result . total )
} )
Em vez de fornecer um objeto, você pode fornecer uma string usando uma forma abreviada de JSON. Na verdade, você pode fornecer ambos:
seneca . act ( 'cmd:salestax' , { net : 100 } , function ( err , result ) {
console . log ( result . total )
} )
Esta é uma maneira muito conveniente de combinar um padrão e dados de parâmetro .
A maneira de construir sistemas Node.js é construir muitos pequenos processos. Aqui está uma ótima palestra explicando por que você deve fazer isso: Programmer Anarchy.
Sêneca torna isso muito fácil. Vamos colocar a configuração da rede em seu próprio processo:
seneca . add ( { cmd : 'config' } , function ( msg , done ) {
var config = { rate : 0.23 }
var value = config [ msg . prop ]
done ( null , { value : value } )
} )
seneca . listen ( )
O método listen
inicia um servidor web que escuta mensagens JSON. Quando chegam, são submetidos à instância local do Sêneca e executados como ações da forma normal. O resultado é então retornado ao cliente como resposta à solicitação HTTP. Seneca também pode ouvir ações por meio de um barramento de mensagens.
Sua implementação do código de configuração permanece a mesma .
O código do cliente fica assim:
seneca . add ( { cmd : 'salestax' } , function ( msg , done ) {
seneca . act ( { cmd : 'config' , prop : 'rate' } , function ( err , result ) {
var rate = parseFloat ( result . value )
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
} )
seneca . client ( )
seneca . act ( 'cmd:salestax,net:100' , function ( err , result ) {
console . log ( result . total )
} )
No lado do cliente, chamar seneca.client()
significa que o Seneca enviará quaisquer ações que não possa corresponder localmente pela rede. Neste caso, o servidor de configuração corresponderá ao padrão cmd:config
e retornará os dados de configuração.
Novamente, observe que seu código de imposto sobre vendas não muda . Não é necessário saber de onde vem a configuração, quem a fornece ou como.
Você pode fazer isso com todos os comandos.
O problema dos requisitos de negócios é que eles não respeitam o bom senso, a lógica ou a estrutura ordenada. O mundo real é confuso.
Em nosso exemplo, digamos que alguns países tenham uma taxa única de imposto sobre vendas e outros tenham uma taxa variável, que depende da localidade ou da categoria do produto.
Aqui está o código. Eliminaremos o código de configuração deste exemplo.
// fixed rate
seneca . add ( { cmd : 'salestax' } , function ( msg , done ) {
var rate = 0.23
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
// local rates
seneca . add ( { cmd : 'salestax' , country : 'US' } , function ( msg , done ) {
var state = {
'NY' : 0.04 ,
'CA' : 0.0625
// ...
}
var rate = state [ msg . state ]
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
// categories
seneca . add ( { cmd : 'salestax' , country : 'IE' } , function ( msg , done ) {
var category = {
'top' : 0.23 ,
'reduced' : 0.135
// ...
}
var rate = category [ msg . category ]
var total = msg . net * ( 1 + rate )
done ( null , { total : total } )
} )
seneca . act ( 'cmd:salestax,net:100,country:DE' , function ( err , result ) {
console . log ( 'DE: ' + result . total )
} )
seneca . act ( 'cmd:salestax,net:100,country:US,state:NY' , function ( err , result ) {
console . log ( 'US,NY: ' + result . total )
} )
seneca . act ( 'cmd:salestax,net:100,country:IE,category:reduced' , function ( err , result ) {
console . log ( 'IE: ' + result . total )
} )
Nesse caso, você fornece implementações diferentes para padrões diferentes. Isso permite isolar a complexidade em locais bem definidos. Isso também significa que você pode lidar com casos especiais com muita facilidade.
A organização Senecajs incentiva a participação. Se você acha que pode ajudar de alguma forma, seja com relatórios de bugs, documentação, exemplos, testes extras ou novos recursos, sinta-se à vontade para criar um problema ou, melhor ainda, enviar uma solicitação pull. Para obter mais informações sobre contribuição, consulte nosso guia de contribuição.
Para executar testes localmente,
npm run test
Para obter um relatório de cobertura,
npm run coverage; open docs/coverage.html
Copyright (c) 2010-2018 Richard Rodger e outros colaboradores; Licenciado pelo MIT .