README
nova-faas
nova-faas is a node.js library that helps you to implement microservices and scalability over rabbitmq service discovery.
- Read CHANGELOG.md
- Read PLUGINS.md
Advantages
- Think better! Think KISS!
- Configuration-driven oriented framework
- Only one monolithic project in your github
- Only one monolithic project to maintain
- Start only the services you want with configuration file
- Agnostics commands and queries, they just had to be promises
- Tests are isolate, because of the structure
- Deploy and scale your microservices like you want
- Automatic services discovery! Thanks rabbitmq
Installation
$ npm i -S nova-faas
Description
Beware, you need a rabbitmq running in localhost for all those examples.
Server
The server is the main program, he needs to be start at first. Because the project is configuration-driven, you only need those lines of code to start all selected microservices in a row.
Glob patterns
This syntax will load all pomises in this dirpath and attach a 1:1 queue for execute the promise. And attach two 1:N queues (on for success event, and the second for error event).
server.use(path.resolve(__dirname, '../test/data/commands/*.js'))
Simple server
const path = require('path')
const Server = require('nova-faas').Server
const server = new Server()
// all options from servicebus (see npm)
const options = {
host: 'localhost',
port: 5672,
user: 'guest',
pass: 'guest',
timeout: 2000,
heartbeat: 10
}
server
.use(path.resolve(__dirname, '../test/data/commands/*.js'))
.use(path.resolve(__dirname, '../test/data/queries/*.js'))
.start(options)
server.on('error', error => {
console.log('server error')
console.log(error)
})
server.on('ready', () => {
console.log('server connected')
})
Service
A service is the base element of the system, it's like a microservice or a task. The application result of a service will automaticaly:
- Send an event on the bus in case of success
- Send an event on the bus in case of error
You will never have to use this class, Command and Query extend it.
Command
- "A result" is either a successful application of the command, or an exception.
- Because it extends Service, success event or error event will be automaticaly send on the bus.
How to create a Command ?
Step 1
You need to create a file who contains "Command" in his name.
path/you/want/BasicNopeCommand.js
Step 2
You need to module.exports a promise.
const handler = function () {
return new Promise((resolve, reject) => {
resolve()
})
}
module.exports = handler
Query
From the library point of view a query is the same as a command, but because of queries roles in the architecture, this time data will be return.
- "A result" is either data, or an exception
- Because it extends Service, success event or error event will be automaticaly send on the bus
How to create a Query ?
Step 1
You need to create a file who contains "Query" in his name.
path/you/want/BasicNopeQuery.js
Step 2
You need to module.exports a promise.
const handler = function () {
return new Promise((resolve, reject) => {
resolve({data: true})
})
}
module.exports = handler
Now it's time to start the server
Classic start:
$ node examples/server.js
But, you can run the server in debug mode.
$ DEBUG=nova:* node examples/server.js
Client
It's time to learn how to link all those services and events together, let's me introduce the Client object.
Definitions
A client could be wherever you need it to be, even on another server, or behind a hapiJS/Express server, or why not in another Server.
You will have three patterns to use the server events bus.
Sender/Receiver pattern
The Send / Receive object pair uses a direct exchange (1:1) inside of RabbitMQ
Publisher/Subscriber pattern
The Publish / Subscribe object pair uses a fanout exchange (1:N) inside of RabbitMQ, allowing you to have as many subscribers as you need. Think of pub/sub as an event that gets broadcast to anyone that cares, or no one at all if no one is listening.
Request/Response pattern
The request/response pair uses a "topic" exchange. With a request/response setup, you can send a request for information and respond to it.
Send a Query
- When the server start and load your handlers, receivers are created in the server.
- Sender client is a classic fire and forget on the bus. In return you will have only a result who informs you if the command or the query has been executed succesfully or not.
Create a file client-sender.js, and and this code in:
const Client = require('nova-faas').Client
const client = new Client()
// all options from servicebus (see npm)
const options = {
host: 'localhost',
port: 5672,
user: 'guest',
pass: 'guest',
timeout: 2000,
heartbeat: 10
}
client
.subscribe('BasicNopeQuery.Success', (result) => {
console.log('success', result)
client.close()
})
.subscribe('BasicNopeQuery.Error', (result) => {
console.log('error', result)
client.close()
})
.start(options)
client.on('error', error => {
console.log('client error')
console.log(error)
})
client.on('ready', () => {
console.log('client connected')
client.send('BasicNopeQuery', {message: 'This is a query'})
})
Nota
- The pattern BasicNopeQuery.* will receive Error and Success event for one specific Query.
- The pattern *.Error will receive Errors for all Commands and Queries.
Now it's time to start the client-send
Classic start:
$ node examples/client-sender.js
But, you can run the client in debug mode.
$ DEBUG=nova:* node examples/client-sender.js
Result will be:
{ type: 'Query',
name: 'BasicNopeQuery',
event: 'BasicNopeQuery.Success',
params: { message: 'This is a query' },
exectime: 1004,
result: { data: true } }
Request a Query
- When the client start a specific queue is created on the server.
- The server will have in the data received an automatic header to help him answering the client who called.
- Events succes or error are also published.
Create a file client-request.js, and and this code in:
const Client = require('nova-faas').Client
const client = new Client()
client
.subscribe('BasicNopeQuery.Success', (result) => {
console.log('success', result)
client.close()
})
.subscribe('BasicNopeQuery.Error', (result) => {
console.log('error', result)
client.close()
})
.start()
client.on('error', error => {
console.log('client error')
console.log(error)
})
client.on('ready', () => {
console.log('client connected')
client.request('BasicNopeQuery', {message: 'This is a query'}, (data) => {
console.log('result', data)
})
})
Result from the success event will be:
success { type: 'Query',
name: 'BasicNopeQuery',
event: 'BasicNopeQuery.Success',
params:
{ message: 'This is a query',
__headers:
{ 'x-client-id': 'Client.Response.38057d6b-bd49-4b4b-9727-35146c43789a',
'x-request-id': 'cb0d0e12-8f8c-4267-a20f-fb05c653aba6' } },
exectime: 1001,
result: { data: true } }
Result from the response callback will be:
result { type: 'Query',
name: 'BasicNopeQuery',
event: 'BasicNopeQuery.Success',
params: { message: 'This is a query' },
exectime: 1001,
result: { data: true } }