@beignet/core
Version:
Core framework primitives for Beignet
143 lines • 5.22 kB
TypeScript
import { type EventPublishOptions } from "../events/index.js";
import type { DomainEventDef, EventBusPort, InferEventPayload } from "./events.js";
type MaybePromise<T> = T | Promise<T>;
/**
* Work function executed inside a Unit of Work.
*
* `tx` contains transaction-scoped app ports, usually repositories plus
* side-effect recorders such as `events`, `jobs`, or `audit`.
*/
export type UnitOfWorkCallback<TxPorts, Result> = (tx: TxPorts) => MaybePromise<Result>;
/**
* A transaction boundary for application workflows.
*
* The transaction ports are app-owned. A database adapter can provide
* transaction-scoped repositories, while test or in-memory adapters can use
* `createNoopUnitOfWork`.
*/
export interface UnitOfWorkPort<TxPorts> {
/**
* Run application work with transaction-scoped ports.
*
* Durable implementations should commit only if this callback resolves.
*/
transaction<Result>(work: UnitOfWorkCallback<TxPorts, Result>): Promise<Result>;
}
/**
* Hooks for `createNoopUnitOfWork(...)`.
*/
export interface NoopUnitOfWorkOptions<TxPorts> {
/**
* Runs after the callback completes successfully.
*
* Use this to flush buffered domain events after the work has committed.
*/
afterCommit?: (tx: TxPorts) => MaybePromise<void>;
/**
* Runs after the callback throws.
*
* Use this to clear buffers or release test resources. The original error is
* rethrown after this hook runs.
*/
afterRollback?: (error: unknown, tx: TxPorts) => MaybePromise<void>;
}
/**
* Best-effort observers for `createObservedUnitOfWork(...)`.
*/
export interface ObservedUnitOfWorkOptions {
/**
* Runs after the wrapped Unit of Work resolves successfully.
*
* Observer failures are isolated and never reject the committed operation.
*/
afterCommit: () => MaybePromise<void>;
/**
* Receives an `afterCommit` failure. Failures from this observer are also
* isolated.
*/
onObserverError?: (error: unknown) => MaybePromise<void>;
}
/**
* Domain event captured by a buffered event recorder.
*/
export interface RecordedDomainEvent {
/**
* Event definition used to validate the payload before publishing.
*/
event: DomainEventDef;
/**
* Stable event name.
*/
eventName: string;
/**
* Unparsed payload recorded during the transaction.
*/
payload: unknown;
/** Optional metadata propagated when the event is flushed. */
options?: EventPublishOptions;
}
/**
* Transaction-scoped port used to record domain events.
*
* Use cases record events here during the Unit of Work. The adapter decides
* whether to publish after commit, enqueue through an outbox, or buffer for a
* test assertion.
*/
export interface DomainEventRecorderPort {
/**
* Record a domain event payload.
*/
record<E extends DomainEventDef>(event: E, payload: InferEventPayload<E>, options?: EventPublishOptions): Promise<void> | void;
}
/**
* In-memory event recorder that can be inspected, cleared, or flushed.
*/
export interface BufferedDomainEventRecorder extends DomainEventRecorderPort {
/**
* Return recorded events without clearing them.
*/
entries(): readonly RecordedDomainEvent[];
/**
* Remove all recorded events.
*/
clear(): void;
/**
* Validate and publish all recorded events to an event bus in FIFO order.
*/
flush(eventBus: EventBusPort): Promise<void>;
}
/**
* Create a simple Unit of Work implementation for tests, in-memory adapters,
* and infrastructure that already handles transactions elsewhere.
*
* This helper does not create database transactions. It gives applications the
* same UOW shape everywhere and runs commit/rollback hooks around the callback.
*
* @param txPortsOrFactory - Transaction-scoped ports or a factory that creates
* them per transaction call.
* @param options - Optional commit and rollback hooks.
* @returns A Unit of Work port with no durable transaction semantics.
*/
export declare function createNoopUnitOfWork<TxPorts>(txPortsOrFactory: TxPorts | (() => TxPorts), options?: NoopUnitOfWorkOptions<TxPorts>): UnitOfWorkPort<TxPorts>;
/**
* Decorate a Unit of Work with an isolated post-commit observer.
*
* The observer runs only after the wrapped transaction resolves. Its failure
* cannot turn a committed operation into an apparent transaction failure.
* Use the observer to schedule best-effort follow-up work; durable side
* effects still belong inside the transaction through an outbox.
*/
export declare function createObservedUnitOfWork<TxPorts>(options: {
unitOfWork: UnitOfWorkPort<TxPorts>;
} & ObservedUnitOfWorkOptions): UnitOfWorkPort<TxPorts>;
/**
* Create a recorder that buffers domain events until the caller flushes them.
*
* Unit of Work adapters commonly flush this recorder from an `afterCommit`
* hook so events are not published when the work rolls back.
*
* @returns A buffered domain event recorder for tests or Unit of Work adapters.
*/
export declare function createDomainEventRecorder(): BufferedDomainEventRecorder;
export {};
//# sourceMappingURL=unit-of-work.d.ts.map