Messages

The definition of the messages is part of the NPM package @tmorin/ceb-messaging-core.

Anatomy of a message

A message is a data structure composed of three properties: kind, headers and body.

PropertyDescription
kindProvide the intention of the message producer.
headersProvide information to the messaging system to handle the message. The headers are a set of arbitrary key/value entries. Two entries are expected for each message: the messageType and the messageId.
bodyAn arbitrary set of information expected by the receiver.

The Message

The commands

A Command is a data structure matching the Message one.

PropertyDescription
kindThe value is always command.
headers.messageTypeHelp the messaging system to route the message to the right command handler.

The Command

Commands are handled by Command Handlers. By convention, a command type should only be processed by one Command Handler implementation and process only once. The main purpose of a command handler is to interact with model artifacts to mutate the state of a system. The underlying side effects can be described by a Result and/or a set of Events.

The queries

A Query is a data structure matching the Message one.

PropertyDescription
kindThe value is always query.
headers.messageTypeHelp the messaging system to route the message to the right query handler.

The Query

Queries are handled by Query Handlers. By convention, a query type can be processed by different Command Handlers implementations and process more than once. The main purpose of a query handler is to interact with model artifacts to gather data and build a view of the system's state. The view is finally sent back to the requester within a Result.

The results

A Result is a data structure matching the Message one.

PropertyDescription
kindThe value is always result.
headers.messageTypeHelp requester to handle the result, i.e. success vs failure.
headers.originalMessageIdIt's the messageId of the related command or query. It helps the requester to distinguish to which command or query a result belongs to.

The Result

The events

An Event is a data structure matching the Message one.

PropertyDescription
kindThe value is always event.
headers.messageTypeHelp the messaging system to route the message to the right event listeners.

The Event

Events are listened by Event Listeners. By convention, an event type can be listened by many Event listeners. The main purpose of an event listener is to react on the event.

Message Construction

Messages are simple vanilla JavaScript objects. For instance to create a command Greeting, the following snippet is enough.

const greetingCmd = {
  kind: "command",
  headers: {
    messageType: "Greeting",
    messageId: "command-0"
  },
  body: "Hello, World!"
}

However, manual creation of messages is error-prone and not really DRY. Therefore, a builder is provided to, at least, build messages which are technically valid.

Creation of commands using the MessageBuilder:

import { MessageBuilder } from "@tmorin/ceb-messaging-core"

// create a command
const aCommandA = MessageBuilder.command("CommandA")
  // set a custom identifier
  .identifier("command-1")
  // add an header entry
  .headers({ k1: "v1" })
  // set a body
  .body("a body")
  // build the result
  .build()

Creation of queries using the MessageBuilder:

import { MessageBuilder } from "@tmorin/ceb-messaging-core"

// create a query
const aQueryA = MessageBuilder.query("QueryA")
  // set a custom identifier
  .identifier("query-1")
  // add an header entry
  .headers({ k1: "v1" })
  // set a body
  .body("a body")
  // build the result
  .build()

Creation of events using the MessageBuilder:

import { MessageBuilder } from "@tmorin/ceb-messaging-core"

// create an event
const anEventA = MessageBuilder.event("EventA")
  // set a custom identifier
  .identifier("event-1")
  // add an header entry
  .headers({ k1: "v1" })
  // set a body
  .body("a body")
  // build the result
  .build()

Creation of results using the MessageBuilder:

import { MessageBuilder } from "@tmorin/ceb-messaging-core"

// create a basic command
const commandA = MessageBuilder.command("CommandA").build()

// create a result
const resultA = MessageBuilder.result(commandA)
  // override the message type, by default it's `result`
  .type("custom-type")
  // set a custom identifier
  .identifier("result-1")
  // add an header entry
  .headers({ k1: "v1" })
  // set a body
  .body("a body")
  // build the result
  .build()