Un kit de herramientas de Node.js para arquitecturas de microservicios
Este módulo de código abierto está patrocinado y respaldado por Voxgig. |
---|
Seneca es un conjunto de herramientas para escribir microservicios y organizar la lógica empresarial de su aplicación. Puede dividir su aplicación en "cosas que suceden", en lugar de centrarse en modelos de datos o gestionar dependencias.
Séneca proporciona,
Coincidencia de patrones: una forma maravillosamente flexible de manejar los requisitos comerciales
Independencia del transporte: cómo llegan los mensajes al servidor correcto no es algo de lo que deba preocuparse
Madurez: 8 años en producción (antes lo llamábamos microservicios ), pero una vez fue eliminado por un rayo.
además: un ecosistema amplio y profundo de complementos
libro: una guía para diseñar arquitecturas de microservicios: taomicro
Utilice este módulo para definir comandos que funcionen tomando algo de JSON y, opcionalmente, devolviendo algo de JSON. El comando a ejecutar se selecciona mediante la coincidencia de patrones en el JSON de entrada. Hay conjuntos de comandos integrados y opcionales que le ayudan a crear productos mínimos viables: almacenamiento de datos, gestión de usuarios, lógica distribuida, almacenamiento en caché, registro, etc. Y puede definir su propio producto dividiéndolo en un conjunto de comandos: " cosas que pasan". Eso es todo.
Si está utilizando este módulo y necesita ayuda, puede:
Si es nuevo en Seneca en general, eche un vistazo a senecajs.org. Tenemos de todo, desde tutoriales hasta aplicaciones de muestra para ayudarle a empezar a utilizarlo rápidamente.
La fuente de Seneca se puede leer con anotaciones ejecutando npm run annotate
. Se generará una versión anotada de cada archivo en ./docs/
.
Para instalar a través de 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 ejecutar normalmente, digamos en un contenedor, use
$ node microservice.js
(donde microservice.js
es un archivo de script que usa Seneca). Los registros se generan en formato JSON para que pueda enviarlos a un servicio de registro.
Para ejecutar en modo de prueba, con registros de depuración completos y legibles por humanos, utilice:
$ node microservice.js --seneca.test
Para que no importe,
Siempre que algún comando pueda manejar un documento JSON determinado, está bien.
He aquí un ejemplo:
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 )
} )
En este código, cada vez que Seneca ve el patrón {cmd:'salestax'}
, ejecuta la función asociada con este patrón, que calcula el impuesto sobre las ventas. No hay nada especial en la propiedad cmd
. Es simplemente la propiedad que queremos que coincida con el patrón. ¡Podrías buscar foo
para todas las preocupaciones de Séneca! ¡Sí!
El método seneca.add
agrega un nuevo patrón y la función que se ejecutará cada vez que ocurra ese patrón.
El método seneca.act
acepta un objeto y ejecuta el comando, si lo hay, que coincida.
¿De dónde proviene la tasa del impuesto sobre las ventas? Intentémoslo de nuevo:
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 )
} )
El comando config
le proporciona su configuración. Esto es genial porque no importa de dónde obtiene la configuración: codificada, sistema de archivos, base de datos, servicio de red, lo que sea. ¿Tuviste que definir una API de abstracción para que esto funcionara? No.
Hay un poco de verbosidad aquí, pero demasiada, ¿no crees? Arreglemos eso:
seneca . act ( 'cmd:salestax,net:100' , function ( err , result ) {
console . log ( result . total )
} )
En lugar de proporcionar un objeto, puede proporcionar una cadena utilizando una forma abreviada de JSON. De hecho, puedes proporcionar ambos:
seneca . act ( 'cmd:salestax' , { net : 100 } , function ( err , result ) {
console . log ( result . total )
} )
Esta es una manera muy conveniente de combinar un patrón y datos de parámetros .
La forma de construir sistemas Node.js es construir muchos procesos pequeños. Aquí hay una excelente charla que explica por qué debería hacer esto: Anarquía del programador.
Séneca hace que esto sea realmente fácil. Pongamos la configuración en la red en su propio proceso:
seneca . add ( { cmd : 'config' } , function ( msg , done ) {
var config = { rate : 0.23 }
var value = config [ msg . prop ]
done ( null , { value : value } )
} )
seneca . listen ( )
El método listen
inicia un servidor web que escucha mensajes JSON. Cuando llegan, se envían a la instancia local de Seneca y se ejecutan como acciones de la forma normal. Luego, el resultado se devuelve al cliente como respuesta a la solicitud HTTP. Seneca también puede escuchar acciones a través de un bus de mensajes.
Su implementación del código de configuración sigue siendo la misma .
El código del cliente se ve así:
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 )
} )
En el lado del cliente, llamar a seneca.client()
significa que Seneca enviará cualquier acción que no pueda coincidir localmente a través de la red. En este caso, el servidor de configuración coincidirá con el patrón cmd:config
y devolverá los datos de configuración.
Nuevamente, observe que su código de impuesto sobre las ventas no cambia . No necesita saber de dónde viene la configuración, quién la proporciona ni cómo.
Puedes hacer esto con cada comando.
Lo que pasa con los requisitos empresariales es que no respetan el sentido común, la lógica o la estructura ordenada. El mundo real es complicado.
En nuestro ejemplo, digamos que algunos países tienen una tasa de impuesto sobre las ventas única y otros tienen una tasa variable, que depende de la localidad o de la categoría de producto.
Aquí está el código. Eliminaremos el código de configuración para este ejemplo.
// 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 )
} )
En este caso, proporciona diferentes implementaciones para diferentes patrones. Esto le permite aislar la complejidad en lugares bien definidos. También significa que puede abordar casos especiales con mucha facilidad.
La organización Senecajs fomenta la participación. Si cree que puede ayudar de alguna manera, ya sea con informes de errores, documentación, ejemplos, pruebas adicionales o nuevas funciones, no dude en crear un problema o, mejor aún, enviar una solicitud de extracción. Para obtener más información sobre la contribución, consulte nuestra guía de contribución.
Para ejecutar pruebas localmente,
npm run test
Para obtener un informe de cobertura,
npm run coverage; open docs/coverage.html
Copyright (c) 2010-2018 Richard Rodger y otros colaboradores; Con licencia del MIT .