Commands

Commands are a type of message that represents an instruction to do work. These can be technical instructions such as BackupDatabase, ScaleOutLoadBalancer or modelled after your business domains like PlaceOrder, ShipPackage.

Use plain english when naming commands. This helps understanding what a command will do once it's processed.

To implement a command, extend the Command class and add any relevant fields

import { Command } from '@node-ts/bus-messages'

export class ChargeCreditCard extends Command {
  /**
   * A unique name that identifies the message. This should be done in namespace style syntax,
   * ie: organisation/domain/command-name
   */
  $name = 'my-app/accounts/charge-credit-card'

  /**
   * The contract version of this message. This can be incremented if this message changes the
   * number of properties etc to maintain backwards compatibility
   */
  $version = 1

  /**
   * Create a charge on a credit card
   * @param creditCardToken Identfies the card to charge
   * @param amountThe amount, in USD, to charge the card
   */
  constructor (
    readonly creditCardToken: string,
    readonly amount: number
  ) {
  }
}

A commands are sent to a single service for processing, and generally result in the publication of one or more events.

It's useful to declare all of your messages in a central package that can be shared amongst your publisher and subscriber services.

Sending a command

Commands should only be processed by a single service, unlike events which may have multiple subscribers. Command processors usually receive a command, process it, and emit an event as a result of the operation completing.

Use .send() to send a command:

const chargeCreditCard = new ChargeCreditCard('abc', 123)

// Send a command. This will be handled by a single subscriber
await bus.send(chargeCreditCard)

// Send a message along with a set of attributes
await bus.send(
  chargeCreditCard,
  { correlationId: 'tok-1adsfas-df1' }
)

Handling a Command

Comments get processed by a Handler. This is a function or a class function that receives the message as a parameter and performs an operation. When the handler returns the message is deleted from the queue.

Implementing a function based handler

import { handlerFor } from '@node-ts/bus-core'

// Function based handler
const chargeCreditCardHandler = handlerFor(
  ChargeCreditCard,
  async (event: ChargeCreditCard) => {
    // ...
  }
)

Implementing a class based handler

import { Handler } from '@node-ts/bus-core'

// Class based handler
class ChargeCreditCardHandler implements Handler<ChargeCreditCard> {
  messageType = ChargeCreditCard
  
  async handle (event: ChargeCreditCard) {
    // ...
  }
}

Register the handler with the bus configuration

  const bus = await Bus.configure()
    .withHandler(chargeCreditCardHandler) // Function based handler
    .withHandler(ChargeCreditCardHandler) // Class function based handler
    .initialize()

Remember to .start() the bus to start handling messages

await bus.start()

Last updated