1.0.1 • Published 7 months ago

outbox-nestjs v1.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
7 months ago

Transactional Outbox NestJs library

This library implements the transactional outbox pattern using Nestjs and Typeorm. For more information on transactional outbox patter, check here

Currently, the library supports event publishing to Kafka and RabbitMQ

Installation

npm install outbox-nestjs -s

Context

Outbox pattern guarantees data consistency for services that, as part of a business process, needs to persist an entity and also publish and event/message to a broker.

To achieve this, the message is stored in the database as part of the transaction that updates the business entities. A separate process then sends the messages to the message broker. This library implements this using the polling publisher pattern

The message that is persisted in the database is called the Outbox entity which looks like below

Outbox Entity

@Entity()
export class Outbox {

   @PrimaryGeneratedColumn(`uuid`)
   id: string;

   @Column()
   aggregateId: string

   @Column()
   messagePayload: string

   @Column()
   eventType: string

   @CreateDateColumn()
   createdAt: Date
}
Outbox Entity PropertiesMeaning
idUUID primary key for the outbox entity
aggregateIdThis represents the Id of the aggregate. i:e entity that is being saved along with the outbox
messagePayloadMessage payload to be published. Should be in JSON string format
eventTypeThis represents the type of event. i:e OrderProcessedEvent
createdAtWhen the event was created. Event are published in reverse chronological order. i:e FIFO

Configuration

App Module configuration

In your NestJs application, navigate the app.module.ts and make the following configuration. The configuration can either one of the two below

  • Kafka Publisher configuration
  • RabbitMQ configuration

However, regardless of the event queue, there are two common configuration properties as can be seen below

OutboxModule PropertiesMeaning
queueTypeThe type of message queue to publish the message. This can either KAFKA or RABBITMQ
datasourceThis is the datasource configured for your application for connecting to the underlying database

Kafka Publisher Configuration

If you will be publishing to Kafka, your configuration should look something like this below. We are going to add the OutboxModule in the imports section of the@Module decorator

import {MessageQueueType, OutboxModule} from "outbox-nestjs";

imports: [
  OutboxModule.forRoot({
    kafkaOptions: {
      clientId: `test-application`,
      brokers: [`localhost:9092`],
      topic: `outbox-topic`
    },
    queueType: MessageQueueType.KAFKA,
    datasource: connectionSource
  }),
]
Kafka Options PropertiesMeaning
clientIdThis is the name of your service or whatever you want to call your client
brokersThis the broker url(s). Supports an array for multiple brokers
topicThe topic name to send your messages to

RabbitMQ Configuration

If you are using RabbitMQ, your configuration should look something like this below. We are going to add the OutboxModule in the imports section of the @Module decorator

import {MessageQueueType, OutboxModule} from "outbox-nestjs";

imports: [
  OutboxModule.forRoot({
    rabbitOptions: {
      exchange: `sample-exchange`,
      connectionString: `amqp://guest:guest@localhost:5672`
    },
    queueType: MessageQueueType.RABBIT_MQ,
    datasource: connectionSource
  }),
]
Kafka Options PropertiesMeaning
exchangeThis is the rabbitMQ exchange to message publishing
connectionStringThis is the connection string for connecting to rabbitMQ

Datasource Configuration

This library requires a datasource in order to fetch data from the outbox table. Typically, your datasource configuration would look something like below

import { Outbox } from "outbox-nestjs";


const config: TypeOrmModuleOptions = {
   type: "xxxx",
   host: `xxx`,
   port: xxxx,
   username: `xxxx`,
   password: `xxxx`,
   database: `xxxx`,
   entities: [`xxx`, Outbox],
   migrationsRun: true,
   migrations: ["dist/domain/migrations/*{.ts,.js}"],
   autoLoadEntities: true,
   synchronize: true,
   logger: "advanced-console"
};

The important part of the above configuration is that we have to add the Outbox entity to the entities array

Polling publisher

The library uses nestjs ScheduleModule to poll and publish to the message broker. You need to initiate the module. In app.module.ts, add the following

@Module({
   imports: [
      ScheduleModule.forRoot()
   ],
})

Database Migrations

The Typeorm configuration from above has synchronize:true, which would create the Outbox table in the database, however, DO NOT USE THIS IN PRODUCTION!!!

You should create the Outbox table in your database, choosing the right data type for each property

Other Considerations

There are some details to be aware of when using this library

  • The default polling interval is 1 Second for now
  • This library would try to publish the events at least once. However, there can be cases where an event is published more than once.
    Your clients should be Idempotent
  • While the implementation tries to publish the events in order which they arrive, the order may not be guaranteed ;)

Author

Nriagu Chidubem