Inversion

The definition of the Inversion module is part of the NPM package @tmorin/ceb-messaging-inversion.

Command and Query handlers as well as Event listeners can be discovered and managed by Inversion on the container initialization. The module MessagingModule takes care of the discovery, registration and also disposition of handlers and listeners at the container end of life.

Discoverable Command handlers must match the DiscoverableCommandHandler interface.

import {
  Command,
  Event,
  MessageBuilder,
  Result,
} from "@tmorin/ceb-messaging-core"
import { DiscoverableCommandHandler } from "@tmorin/ceb-messaging-inversion"

// create the handler using the "implementation" way
export class GreetSomebodyHandler
  implements
    DiscoverableCommandHandler<
      Command<string>,
      Result<string>,
      Array<Event<string>>
    >
{
  // the type of the Command to handle
  type = "GreetSomebody"

  // the handler
  handler(command: Command<string>) {
    // create the greeting text
    const result = MessageBuilder.result(command)
      .body(`Hello, ${command.body}!`)
      .build()
    // create the event
    const events = [
      MessageBuilder.event("SomeoneHasBeenGreeted").body(command.body).build(),
    ]
    // return the output
    return { result, events }
  }
}

Discoverable Query handlers must match the DiscoverableQueryHandler interface.

import { MessageBuilder, Query, Result } from "@tmorin/ceb-messaging-core"
import { DiscoverableQueryHandler } from "@tmorin/ceb-messaging-inversion"

// create the handler using the "implementation" way
export class WhatTimeIsItHandler
  implements DiscoverableQueryHandler<Query<void>, Result<string>>
{
  // the type of the Query to handle
  type = "WhatTimeIsIt"

  // the handler
  handler(query: Query<void>) {
    return MessageBuilder.result(query).body(new Date().toISOString()).build()
  }
}

Discoverable Event listeners must match the DiscoverableEventListener interface.

import { Event } from "@tmorin/ceb-messaging-core"
import { DiscoverableEventListener } from "@tmorin/ceb-messaging-inversion"

// create the listener using the "object" way
export const SOMEONE_HAS_BEEN_GREETED_LISTENER: DiscoverableEventListener<
  Event<string>
> = {
  // the type of the Event to handle
  type: "SomeoneHasBeenGreeted",
  // the handler
  listener: (event: Event<string>) => {
    console.info(`${event.body} has been greeted`)
  },
}

To be discoverable the handlers and listeners must be registered in the Container's Registry with the right Registry Key.

  • DiscoverableCommandHandlerSymbol for the Command handlers
  • DiscoverableQueryHandlerSymbol for the Query handlers
  • DiscoverableEventListenerSymbol for the Event listeners
import { AbstractModule } from "@tmorin/ceb-inversion-core"
import { GreetSomebodyHandler } from "./inversion-discovery-command"
import { WhatTimeIsItHandler } from "./inversion-discovery-query"
import { SOMEONE_HAS_BEEN_GREETED_LISTENER } from "./inversion-discovery-event"
import {
  DiscoverableCommandHandlerSymbol,
  DiscoverableEventListenerSymbol,
  DiscoverableQueryHandlerSymbol,
} from "@tmorin/ceb-messaging-inversion"

// define a "regular" Module
export class DiscoverableStuffModule extends AbstractModule {
  async configure() {
    // register the command handler
    this.registry.registerValue(
      DiscoverableCommandHandlerSymbol,
      new GreetSomebodyHandler()
    )
    // register the query handler
    this.registry.registerFactory(
      DiscoverableQueryHandlerSymbol,
      () => new WhatTimeIsItHandler()
    )
    // register the event listener
    this.registry.registerValue(
      DiscoverableEventListenerSymbol,
      SOMEONE_HAS_BEEN_GREETED_LISTENER
    )
  }
}

Finally, the module MessagingModule and those registering discoverable handlers and listeners must be registered as other modules.

import { ContainerBuilder } from "@tmorin/ceb-inversion-core"
import {
  Gateway,
  GatewaySymbol,
  MessageBuilder,
} from "@tmorin/ceb-messaging-core"
import { DiscoverableStuffModule } from "./inversion-discovery-module"
import { MessagingModule } from "@tmorin/ceb-messaging-inversion"
import { SimpleModule } from "@tmorin/ceb-messaging-simple-inversion"

ContainerBuilder.get()
  // register the module which discovers the handlers and listeners
  .module(new MessagingModule())
  // register the module which provide a Gateway instance
  .module(new SimpleModule())
  // register the module which contain the discoverable handlers and listeners
  .module(new DiscoverableStuffModule())
  .build()
  .initialize()
  .then(async (container) => {
    // resolve the gateway
    const gateway = container.registry.resolve<Gateway>(GatewaySymbol)

    // register an event listener
    gateway.events.subscribe("SomeoneHasBeenGreeted", (event) => {
      console.info(`${event.body} has been greeted`)
    })

    // create and execute the GreetSomebody command
    const greetSomebody = MessageBuilder.command("GreetSomebody")
      .body("World")
      .build()
    const cmdResult = await gateway.commands.execute(greetSomebody)
    console.info(`the greeting text: ${cmdResult.body}`)

    // create and execute the WhatTimeIsIt query
    const whatTimeIsIt = MessageBuilder.query("WhatTimeIsIt")
      .body("World")
      .build()
    const qryResult = await gateway.queries.execute(whatTimeIsIt)
    console.info(`the time: ${qryResult.body}`)

    return container.dispose()
  })
  .catch((e) => console.error(e))