Skip to content

Bsociety/seneca-microservice-good-practices

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 

Repository files navigation

SenecaJS Microservice - Good Practices

npm yarn node

Table of Contents

  1. Packages
  2. Files
  3. Functions
    1. Joi validation
    2. Params formatation/validation
    3. Promises Chaining
    4. Logging
    5. Message sending to another microservice
  4. Responses
    1. In case of Error
    2. In case of Success
  5. Tests
    1. Test file
    2. Mocks
  6. Pull Requests
    1. Recomendation

Packages

$ npm i seneca-merge-validate@latest -S

Files

  • Use specific files for specific responsabilities. Example:
  1. create.js
  2. select.js
  3. update.js
  4. delete.js

Recomendation

  • In any file all lines should respect max 80 chars.

Functions

  • Use private functions for specific responsabilities. Examples: Joi validation, message sending to another microservice, etc.

  • All private functions must return a promise. Exception: Joi validation.

Joi validation

  • The function responsible for defining Joi validation must have the signature getValidateSchema ().

  • This function must have a separator (\n) between field rules. Example:

function getValidateSchema () {
  return {
    type: Joi.string()
    .required()
    .description('the type'),

    number: Joi.number()
    .required()
    .description('the number')
  }
}

Params formatation/validation

  • The params formatation and validation is going to use Seneca Merge-Validate package

  • The validate method must send args, PICK_FIELDS constant, Joi schema defined on getValidateSchema function and options.

  • Example:

const SenecaMergeValidate = require('seneca-merge-validate')
const senecaMergeValidate = SenecaMergeValidate(seneca)
const Joi = senecaMergeValidate.Joi
const PICK_FIELDS = [
  'id'
]

  /* ... */

senecaMergeValidate.validate({
  args,
  pick: PICK_FIELDS,
  schema: getValidateSchema(),
  options: { abortEarly: false }
})

Complete example

Promises Chaining

  • The chaining promises responsible for executing bussiness rules must respect then and catch as the pattern below:
  senecaMergeValidate.validate({ /* ... */ })
    .then(params => yourFunction(params))
    .then(result => done(null, result))
    .catch(err => done(null, {
      status: false,
      message: err && err.message || err
    }))

Logging

  • The log tag must be declared in global scope as the pattern below:
const LOG_TAG = 'LOG::[DOCUMENT | DELETE]'

Message sending to another microservice

  • The function responsible for sending a message to another microservice must respect err, response and logging as the pattern below:
function yourFunction (params) {
  return new Promise((resolve, reject) => {
    const pattern = definePattern()
    const payload = definePayload(params)
    seneca.act(pattern, payload, (err, response) => {
      if (err) {
        seneca.log.fatal(LOG_TAG, err)
        return reject(err)
      }
      if (!response.status) {
        seneca.log.error(LOG_TAG, response)
        return reject(response)
      }
      seneca.log.info(LOG_TAG, response)
      return resolve(response)
    })
  })
}
  • The pattern and payload needed to send a message to another microservice must be defined on specific functions, for each one. Example:
function definePattern () {
  return { role: 'entity', get: 'service', method: 'update' }
}

function definePayload (formattedParams) {
  const params = {
    where: {
      id: formattedParams.id,
      deleted: formattedParams.deleted || false
    }
  }

  return { query: { params, data: { deleted: true } } }
}

Responses

In case of Error

  • Error responses of a microservice must follow the pattern below.

  • This pattern must be used for Joi validation or any other microservice that contains errors.

{
  status: false,
  message: 'Error message here'
}

In case of Success

  • Success responses of a microservice must follow the pattern below. Exceptions: entity, webapi.
{
  status: true,
  result: 'Success result here'
}

Tests

  • The test directory must follow the structure below, using contributor as an example:
├── test
│   ├── assets/
│   ├── mocks/
│   ├── contributor.test.js
│   ├── helpers.js

Test file

  • The file name responsible for testing the microservice must have the microservice name with .test.js. Example: contributor.test.js.

  • It must be only one file.

  • If its a composite name it must be writen as the pattern below:

composite_name.test.js
  • The file must respect the order below:
  1. Create/Upsert
  2. Select
  3. Update
  4. Delete
  • Into each test, pattern and payload constants must be used, example:
describe('Complete Tests', () => {
  describe('Specific test', () => {
    it('Expect Joi validation to detect error on payload', (done) => {
      const pattern = Mock.pattern
      const payload = Mock.invalidPayload.payload
      seneca.act(pattern, payload, (err, response) => {
        try {
          expect(err).to.be.equal(null)
          expect(response.status).to.be.equal(false)
          expect(typeof response.message).to.be.equal('object')
          expect(response.message.name).to.be.equal('ValidationError')
          done(null)
        } catch (err) {
          done(err)
        }
      })
    })
  })
})

Mocks

  • Each responsability contained in the microservice must have a mock file, into mocks directory. Example:
  1. upsert.js
  2. select.js
  3. delete.js

Pull Requests

Recomendation

  • Internal projects should name branches and pull requests with same ID as the Jira tasks. Example:
Jira task: B2-420

Branch: B2-420

Pull Request: B2 420
  • Each pull request and branch created should have the ID of a main task and not sub-tasks.

  • Always use Jira time-trackers on sub-tasks.

  • Each pull request should contain bussiness rules (the task itself), unit/behavior tests and WebAPI endpoints.

Releases

No releases published

Packages

No packages published