@axinom/mosaic-transactional-inbox-outbox
Version:
This library encapsulates the Mosaic based transactional inbox and outbox pattern
112 lines (106 loc) • 3.6 kB
text/typescript
import { MessagingSettings } from '@axinom/mosaic-message-bus-abstractions';
import { randomUUID } from 'node:crypto';
import {
DatabaseClient,
ListenerConfig,
TransactionalMessage,
initializeMessageStorage,
} from 'pg-transactional-outbox';
import {
InboxOutboxLogger,
RabbitMqMessageFields,
RabbitMqMessageProperties,
} from '../common';
import { TransactionalLogMapper } from '../transactional-log-mapper';
/**
* Helper interface for inbox message metadata, that includes the message
* envelope overrides for e.g. the auth_token.
*/
export interface InboxMessageMetadata {
/** overrides for Mosaic Envelope properties (ex: auth_token) */
authToken?: string;
messageVersion?: string;
messageContext?: unknown;
fields?: RabbitMqMessageFields;
properties?: RabbitMqMessageProperties;
[key: string]: unknown;
}
/**
* Add additional data to the message that is going to be stored
* @param metadata Messaging transport related metadata
* @param concurrency Define if the message must be sent in the sequential order it was added or if it can be sent in parallel with others.
* @param lockedUntil The date and time in ISO 8601 "internet time" UTC format (e.g. "2023-10-17T11:48:14Z") until when the message cannot be sent out
* @param segment Can be set to allow processing of messages with different segments in parallel.
* @param createdAt The create date of the message if it is available.
* @param messageId The unique ID of the message. If not specified - random UUID will be used
*/
export interface OptionalInboxData {
metadata?: InboxMessageMetadata;
concurrency?: 'sequential' | 'parallel';
lockedUntil?: string;
segment?: string;
createdAt?: string;
messageId?: string;
}
/**
* Function to store the transactional inbox message
* @param messageId The unique ID of the message
* @param aggregateId The (database) ID of the aggregate type or use the const UNKNOWN_AGGREGATE_ID or MULTIPLE_AGGREGATE_IDS values.
* @param messagingSettings The messaging related settings object
* @param payload The payload of the message
* @param client The database client to use to store the message
* @param optionalData Add additional data to the message that is going to be stored
*/
export type StoreInboxMessage = <T>(
aggregateId: string,
messagingSettings: Pick<MessagingSettings, 'messageType' | 'aggregateType'>,
payload: T,
client: DatabaseClient,
optionalData?: OptionalInboxData,
) => Promise<void>;
/**
* Returns a function to store transactional inbox messages directly for service
* internal messaging.
* See the StoreInboxMessage type for parameter descriptions.
*/
export const setupInboxStorage = (
listenerConfig: ListenerConfig,
logger: InboxOutboxLogger,
config: { logLevel: string },
): StoreInboxMessage => {
const storage = initializeMessageStorage(
listenerConfig,
new TransactionalLogMapper(logger, config.logLevel),
);
return async <T>(
aggregateId: string,
{
aggregateType,
messageType,
}: Pick<MessagingSettings, 'aggregateType' | 'messageType'>,
payload: T,
client: DatabaseClient,
{
metadata,
concurrency,
lockedUntil,
segment,
createdAt,
messageId,
}: OptionalInboxData = {},
) => {
const message: TransactionalMessage = {
id: messageId ?? randomUUID(),
aggregateId,
payload,
aggregateType,
messageType,
metadata,
concurrency: concurrency ?? 'parallel',
lockedUntil,
segment,
createdAt,
};
await storage(message, client);
};
};