@northscaler/message-support

0.2.0 • Public • Published

@northscaler/message-support

Message factories to use in your CQRS or asynchronous, message-based architectures.

Types of messages

This library supports three types of messages:

  • request,
  • response, and
  • event

messages.

Key message features

There are three top-level sections to each message:

  • data: the payload of the message, which is usually an object,
  • meta: data about the message, including
    • id: the unique message id,
    • origin: the location (component & hostname) where the message was created,
    • instant: the instant in time the message was created,
    • traceId: a trace id that can be used for distributed tracing which can be given or is generated, and
    • correlationId: an id that, if present, indicates that something is awaiting a response,
  • error: present if there is an error processing a message, including
    • name: the Error.name value,
    • message: the Error.message value,
    • stack: if so configured (false by default), the Error.stack value,
    • code: the Error.code value, if present,
    • info: the Error.info value, containing contextual information provided with the Error, if present,
    • cause: if so configured (false by default), the Error.cause value, holding the cause of the Error, if present.

In addition, all message types have an action in a type-dependent location.

  • For a request message, it's in the meta.request section.
  • For an event message, it's the meta.event section.
  • For a response message, it's in the meta.response section.

Each message type may include other, type-specific fields as documented.

Custom data

The data section is never inspected, as it is always application-specific. Sometimes, applications need more than just an action property in the message. You are free to add properties anywhere you like after the factory has created the message. Common properties like this include

  • scope, domain or context, indicating some conceptual boundary that only applies to certain message processors and usually placed next to action,
  • type, indicating the type of the entity being manipulated for CRUD operations and is also usually placed next to action, and
  • auth, security, securityContext, user or userContext, holding information about the authenticated user (like user id, roles played, scopes granted, etc) causing the activity and is usually placed directly in the meta section.

Request messages

Use the RequestMessageFactory to create a request message.

const { RequestMessageFactory } = require('@northscaler/message-support').factories
const pkg = require('.../package.json') // or whatever

const factory = new RequestMessageFactory({
  componentName: pkg.name,
  componentVersion: pkg.version
})

const message = factory.create({
  data: { username: 'matthew', email: 'me@me.com' },
  action: 'CREATE_USER',
  traceId: '456',
  correlationId: '789'
})

console.log(JSON.stringify(message, null, 2))

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com"
  },
  "meta": {
    "id": "c6cac4e6-47e4-4e6c-b0eb-95984c04b9f3",
    "instant": "2020-09-18T18:40:52.069Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "request": {
      "action": "CREATE_USER"
    },
    "correlationId": "789"
  }
}
*/

Event messages

Use the EventMessageFactory to create event messages. There are two temporal kinds of event messages, past & future, and two kinds of past events, successful & unsuccessful.

The most common is a success event:

const { CodedError } = require('@northscaler/error-support')
const { EventMessageFactory } = require('../main/factories') // require('@northscaler/message-support').factories
const pkg = require('../../package.json') // or whatever

const TestError = CodedError({ name: 'TestError' })
const NestedTestError = CodedError({ name: 'NestedTestError' })

const factory = new EventMessageFactory({
  componentName: pkg.name,
  componentVersion: pkg.version,
  includeErrorStacks: true,
  includeErrorCauses: true
})

let message = factory.createSuccess({
  data: { username: 'matthew', email: 'me@me.com' },
  action: 'CREATE_USER',
  traceId: '456',
  correlationId: '789'
})

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com"
  },
  "meta": {
    "id": "5cfc0636-c1a8-4878-b2fd-d954db7157ea",
    "instant": "2020-09-18T19:10:56.072Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "event": {
      "did": "CREATE_USER",
      "status": "SUCCESS"
    }
  }
}
*/

Another common one is a failure event:

message = factory.createFailure({
  data: { username: 'matthew', email: 'me@me.com' },
  action: 'CREATE_USER',
  error: new TestError({ message: 'boom', cause: new NestedTestError({ message: 'bang' }) }),
  traceId: '456',
  correlationId: '789'
})

console.log(JSON.stringify(message, null, 2))

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com"
  },
  "error": {
    "name": "TestError",
    "code": "E_TEST",
    "cause": {
      "name": "NestedTestError",
      "code": "E_NESTED_TEST",
      "message": "E_NESTED_TEST: bang",
      "stack": null
    },
    "message": "E_TEST: boom: E_NESTED_TEST: bang",
    "stack": null
  },
  "meta": {
    "id": "e6232595-35cd-461f-9488-a7b69efd31b1",
    "instant": "2020-09-18T19:07:52.359Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "event": {
      "did": "CREATE_USER",
      "status": "FAILURE"
    }
  }
}
*/

You can also send events indicating that something is about to happen:

message = factory.createFuture({
  data: { username: 'matthew', email: 'me@me.com' },
  action: 'CREATE_USER',
  traceId: '456',
  correlationId: '789'
})

console.log(JSON.stringify(message, null, 2))

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com"
  },
  "meta": {
    "id": "d5f024da-2ad9-43a2-aa9c-7d992088db96",
    "instant": "2020-09-18T19:14:46.625Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "event": {
      "will": "CREATE_USER"
    }
  }
}
*/

Response messages

Use the ResponseMessageFactory to create response messages for request messages that include a correlationId, indicating that something is expecting a response after the request is handled. There are two kinds of response messages, successful & unsuccessful.

The most common is a successful response. This example uses the convenience method createSuccessFromRequest.

const { CodedError } = require('@northscaler/error-support')
const { ResponseMessageFactory, RequestMessageFactory } = require('../main/factories') // require('@northscaler/message-support').factories
const pkg = require('../../package.json') // or whatever

const TestError = CodedError({ name: 'TestError' })
const NestedTestError = CodedError({ name: 'NestedTestError' })

const requestFactory = new RequestMessageFactory({
  componentName: pkg.name,
  componentVersion: pkg.version,
  includeErrorStacks: false,
  includeErrorCauses: true
})

const responseFactory = new ResponseMessageFactory({
  componentName: pkg.name,
  componentVersion: pkg.version,
  includeErrorStacks: false,
  includeErrorCauses: true
})

// this is the request to which we're going to send a response message
const request = requestFactory.create({
  data: { username: 'matthew', email: 'me@me.com' },
  action: 'CREATE_USER',
  traceId: '456',
  correlationId: '789'
})

// say the service we delegated to assigned id '246' to the user, so add that to the data
let message = responseFactory.createSuccessFromRequest({ request, data: { ...request.data, id: '246' } })

console.log(JSON.stringify(message, null, 2))

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com",
    "id": "246"
  },
  "meta": {
    "id": "cdd0617b-d6f8-46be-ae84-700b86d7ac30",
    "instant": "2020-09-18T19:25:33.464Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "response": {
      "status": "SUCCESS",
      "elapsedMillis": 134
    },
    "correlationId": "789"
  }
}
*/

Here is a failure response. This example uses the convenience method createFailureFromRequest.

message = responseFactory.createFailureFromRequest({
  request,
  data: request.data,
  error: new TestError({ message: 'boom', cause: new NestedTestError({ message: 'bang' }) })
})

console.log(JSON.stringify(message, null, 2))

/* logs something like:
{
  "data": {
    "username": "matthew",
    "email": "me@me.com"
  },
  "error": {
    "name": "TestError",
    "code": "E_TEST",
    "cause": {
      "name": "NestedTestError",
      "code": "E_NESTED_TEST",
      "message": "E_NESTED_TEST: bang",
      "stack": null
    },
    "message": "E_TEST: boom: E_NESTED_TEST: bang",
    "stack": null
  },
  "meta": {
    "id": "840f5486-fe35-4661-9435-796aa0ce3483",
    "instant": "2020-09-18T19:35:36.181Z",
    "traceId": "456",
    "origin": {
      "component": "@northscaler/message-support",
      "version": "0.1.0-pre.0",
      "hostname": "rocky.local"
    },
    "response": {
      "status": "FAILURE",
      "elapsedMillis": 4
    },
    "correlationId": "789"
  }
}
*/

There is also a more general method called create to which these convenience methods delegate.

Package Sidebar

Install

npm i @northscaler/message-support

Weekly Downloads

2

Version

0.2.0

License

MIT

Unpacked Size

1.21 MB

Total Files

72

Last publish

Collaborators

  • npm_northscaler
  • npm_scispike
  • victorynap