@nestjs-transactional/outbox-microservices 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Golovanov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,311 @@
1
+ # @nestjs-transactional/outbox-microservices
2
+
3
+ [![npm version](https://img.shields.io/npm/v/%40nestjs-transactional%2Foutbox-microservices/alpha?style=flat-square&label=npm)](https://www.npmjs.com/package/@nestjs-transactional/outbox-microservices)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/LICENSE)
5
+
6
+ > Spring Modulith `@Externalized` parity for NestJS — durable,
7
+ > retryable delivery of outbox events to external message brokers via
8
+ > the `@nestjs/microservices` `ClientProxy` abstraction.
9
+
10
+ `MicroservicesEventExternalizer` plugs into `@nestjs-transactional/outbox`
11
+ as the concrete `EventExternalizer` implementation and reuses the
12
+ existing `ClientsModule` registration in your application — one
13
+ package covers every transport `@nestjs/microservices` already
14
+ supports (Kafka, RabbitMQ, NATS, JMS, gRPC, custom).
15
+
16
+ ## Architectural foundation
17
+
18
+ - [ADR-015 — event externalization architecture](../../docs/adr/015-event-externalization-architecture.md)
19
+ explains the design (single-package strategy, structural-port SPI,
20
+ reuse of `ClientsModule`, atomicity / ordering rules).
21
+ - [ADR-016 — externalization reliability semantics with `@nestjs/microservices`](../../docs/adr/016-externalization-reliability-semantics.md)
22
+ documents the silent-success limitation that scopes what this
23
+ package can guarantee out of the box, and the three production
24
+ mitigation strategies.
25
+ - [`docs/architecture/event-externalization.md`](../../docs/architecture/event-externalization.md)
26
+ has diagrams, the end-to-end sequence, the failure-mode table, and
27
+ the Spring Modulith mapping.
28
+
29
+ ### Spring Modulith mapping (at a glance)
30
+
31
+ This package is the NestJS analogue of Spring Modulith's
32
+ `@Externalized` plus its four broker-specific artefacts
33
+ (`spring-modulith-events-kafka`, `-amqp`, `-jms`, `-messaging`)
34
+ collapsed into one. `@Externalized` (in `outbox`) lifts directly
35
+ from Spring's annotation; the broker setup moves from a Spring
36
+ auto-configuration to the user's own `ClientsModule.register()`.
37
+ Function-based `routingKey` and `headers` callbacks replace SpEL
38
+ expressions; bring your own type system. Full table in the
39
+ architecture doc above.
40
+
41
+ ## Status
42
+
43
+ Alpha. Public API may change between 0.x releases. Headers /
44
+ `routingKey` are accepted on `@Externalized` but not yet applied to
45
+ the wire payload — see *Limitations* below. End-to-end coverage
46
+ lives in the externalization examples (`externalization-kafka`,
47
+ `externalization-multi-broker`, `externalization-multi-datasource`,
48
+ `externalization-with-fallback`) and the
49
+ [`e-commerce-orders`](../../examples/e-commerce-orders) flagship —
50
+ see [Worked examples](#worked-examples).
51
+
52
+ ## Important: reliability semantics (read before production use)
53
+
54
+ **Read this before adopting the package in production.** The
55
+ `@nestjs/microservices` `ClientProxy.emit()` API this package depends
56
+ on (per [DD-017](../../docs/dd/017-reuse-clients-module.md)) does NOT propagate broker-side
57
+ delivery failures in a way the externalizer can observe. In
58
+ fire-and-forget mode the Observable returned by `emit()` completes
59
+ when the proxy considers the dispatch *handed off to the transport*,
60
+ not when the broker has *durably acknowledged* the message.
61
+
62
+ Concretely, this means:
63
+
64
+ - A `ClientKafka` configured against an unreachable broker can resolve
65
+ `emit()` successfully, the externalizer reports success, and the
66
+ outbox publication is finalised as `COMPLETED` — even though no
67
+ message ever reached a broker.
68
+ - The same applies to RabbitMQ in default fire-and-forget mode and to
69
+ any other transport `@nestjs/microservices` supports.
70
+ - Silent broker failures bypass the outbox retry / staleness /
71
+ resubmit machinery: there is nothing to retry, because as far as
72
+ this layer is concerned delivery succeeded.
73
+
74
+ The outbox still gives you crash-consistent **enqueueing** of events
75
+ and at-least-once **local listener** delivery. What it does NOT give
76
+ you, in this version, is at-least-once **broker-side** delivery
77
+ through `ClientProxy`.
78
+
79
+ [ADR-016](../../docs/adr/016-externalization-reliability-semantics.md)
80
+ documents the finding in full and lays out the future path
81
+ (broker-aware externalizers using native producers under the same
82
+ `EVENT_EXTERNALIZER` SPI).
83
+ [ADR-015](../../docs/adr/015-event-externalization-architecture.md)
84
+ records why this trade-off is acceptable for the v1 scope —
85
+ [`docs/architecture/event-externalization.md`](../../docs/architecture/event-externalization.md)
86
+ has the full sequence diagram and the failure-mode table.
87
+
88
+ ### Mitigation strategies for production
89
+
90
+ 1. **Configure the underlying `ClientProxy` for stronger
91
+ acknowledgment.** Kafka: `producer.acks: 'all'` plus
92
+ `producer.idempotent: true`. RabbitMQ: confirm-channel via
93
+ `amqp-connection-manager`. NATS: JetStream with explicit ack. The
94
+ package reuses whatever proxy you registered (DD-017) — it does
95
+ not interfere with this configuration.
96
+
97
+ 2. **Combine with consumer-side acknowledgment / inbox patterns.**
98
+ Track processed message ids on the receiving system and surface
99
+ gaps to operators. The outbox publication's listener id plus the
100
+ domain event id is enough to deduplicate.
101
+
102
+ 3. **Wait for the broker-aware externalizer iteration** when neither
103
+ of the above is feasible. The
104
+ [`EVENT_EXTERNALIZER` SPI](../outbox/src/externalization/event-externalizer.ts)
105
+ is stable; native adapters will plug into the same place without
106
+ client-side changes.
107
+
108
+ ## Installation
109
+
110
+ ```bash
111
+ pnpm add @nestjs-transactional/outbox-microservices @nestjs-transactional/outbox @nestjs/microservices
112
+ ```
113
+
114
+ `@nestjs-transactional/core`, `@nestjs/common`, `@nestjs/core`,
115
+ `reflect-metadata`, and `rxjs` are peer dependencies (already present
116
+ in any NestJS application).
117
+
118
+ ## Prerequisites
119
+
120
+ This package does NOT register `ClientProxy` instances — that is your
121
+ job (DD-017). Configure them through the standard
122
+ `@nestjs/microservices` `ClientsModule`:
123
+
124
+ ```typescript
125
+ import { ClientsModule, Transport } from '@nestjs/microservices';
126
+
127
+ @Module({
128
+ imports: [
129
+ ClientsModule.register([
130
+ {
131
+ name: 'KAFKA_CLIENT',
132
+ transport: Transport.KAFKA,
133
+ options: {
134
+ client: { brokers: ['localhost:9092'] },
135
+ },
136
+ },
137
+ ]),
138
+ ],
139
+ })
140
+ export class AppModule {}
141
+ ```
142
+
143
+ `OutboxMicroservicesModule.forRoot({ defaultClient: 'KAFKA_CLIENT' })`
144
+ then resolves the same proxy via `ModuleRef.get(token, { strict: false })`
145
+ when an outbox publication is ready to be externalized — no parallel
146
+ connection pool, no second mental model.
147
+
148
+ ## Basic example
149
+
150
+ ```typescript
151
+ import { Module } from '@nestjs/common';
152
+ import { ClientsModule, Transport } from '@nestjs/microservices';
153
+ import { TransactionalModule } from '@nestjs-transactional/core';
154
+ import { Externalized, OutboxModule } from '@nestjs-transactional/outbox';
155
+ import { OutboxMicroservicesModule } from '@nestjs-transactional/outbox-microservices';
156
+
157
+ @Externalized<OrderPlacedEvent>({
158
+ target: 'orders.placed',
159
+ routingKey: (e) => e.tenantId, // see Limitations — logged, not yet applied
160
+ })
161
+ export class OrderPlacedEvent {
162
+ constructor(
163
+ readonly orderId: string,
164
+ readonly tenantId: string,
165
+ ) {}
166
+ }
167
+
168
+ @Module({
169
+ imports: [
170
+ TransactionalModule.forRoot({ isGlobal: true }),
171
+ ClientsModule.register([
172
+ {
173
+ name: 'KAFKA_CLIENT',
174
+ transport: Transport.KAFKA,
175
+ options: { client: { brokers: ['localhost:9092'] } },
176
+ },
177
+ ]),
178
+ OutboxModule.forRoot({}),
179
+ OutboxModule.forFeature([OrderPlacedEvent]),
180
+ OutboxMicroservicesModule.forRoot({
181
+ defaultClient: 'KAFKA_CLIENT',
182
+ }),
183
+ ],
184
+ })
185
+ export class AppModule {}
186
+ ```
187
+
188
+ When an `OrderPlacedEvent` flows through the outbox the local
189
+ listeners run first; once they succeed the externalizer calls
190
+ `KAFKA_CLIENT.emit('orders.placed', event)`. Failures (broker down,
191
+ client misconfigured, ...) mark the publication `FAILED` and surface
192
+ through `FailedEventPublications.resubmit()` — single-unit atomicity
193
+ per [DD-019](../../docs/dd/019-hybrid-delivery-atomicity.md).
194
+
195
+ ## Multiple clients
196
+
197
+ Register every transport you need under distinct tokens, then point
198
+ each event at its broker via the `client` option on `@Externalized`:
199
+
200
+ ```typescript
201
+ ClientsModule.register([
202
+ { name: 'KAFKA_CLIENT', transport: Transport.KAFKA, options: { ... } },
203
+ { name: 'AMQP_CLIENT', transport: Transport.RMQ, options: { ... } },
204
+ ]),
205
+
206
+ @Externalized({ target: 'orders.placed', client: 'KAFKA_CLIENT' })
207
+ class OrderPlacedEvent { /* ... */ }
208
+
209
+ @Externalized({ target: 'audit', client: 'AMQP_CLIENT' })
210
+ class AuditableEvent { /* ... */ }
211
+ ```
212
+
213
+ A `defaultClient` configured on the module is used when an event
214
+ omits the per-event `client`. Set neither and the externalizer
215
+ rejects the publication with a clear `ExternalizationError` — the row
216
+ is recorded as `FAILED` and the operator can fix the configuration
217
+ and resubmit.
218
+
219
+ ## Multi-dataSource setups
220
+
221
+ This package needs no special configuration when the application
222
+ runs with multiple `OutboxModule.forRoot()` calls (one per
223
+ dataSource — ADR-019 multi-forRoot pattern). A single
224
+ `MicroservicesEventExternalizer` instance covers every dataSource;
225
+ per-DS processors all dispatch through it. Per-broker routing is
226
+ already handled by the per-event `client` parameter shown in
227
+ [*Multiple clients*](#multiple-clients) above — `@Externalized` is
228
+ dataSource-agnostic by design (an event class that lives in the
229
+ `'billing'` dataSource via `OutboxModule.forFeature([Event], { dataSource: 'billing' })`
230
+ still uses the same `client:` token resolution as a default-DS
231
+ event). See [`@nestjs-transactional/outbox` README](../outbox/README.md)
232
+ for the multi-`forRoot` pattern.
233
+
234
+ The module is registered as `@Global()` so the bound
235
+ `EVENT_EXTERNALIZER` is visible to `OutboxModule`'s sibling-imported
236
+ per-DS processors without an explicit import chain.
237
+
238
+ ## Async configuration
239
+
240
+ For `defaultClient` that must be resolved from a `ConfigService`:
241
+
242
+ ```typescript
243
+ OutboxMicroservicesModule.forRootAsync({
244
+ imports: [ConfigModule],
245
+ inject: [ConfigService],
246
+ useFactory: (config: ConfigService) => ({
247
+ defaultClient: config.getOrThrow<string>('outbox.defaultClient'),
248
+ }),
249
+ }),
250
+ ```
251
+
252
+ ## Bootstrap validation
253
+
254
+ By default the module resolves `defaultClient` once at
255
+ `OnApplicationBootstrap` and throws a descriptive error if the token
256
+ is unbound — the misconfiguration surfaces before the first event is
257
+ processed. Disable with `validateOnBootstrap: false` when the
258
+ `ClientProxy` registration is wired by an asynchronous factory that
259
+ finishes after the outbox bootstrap (the lookup is then deferred to
260
+ the first `externalize()` call).
261
+
262
+ ## Limitations
263
+
264
+ - **Headers and `routingKey` are accepted but not applied** to the
265
+ wire payload yet. The `@nestjs/microservices` `ClientProxy.emit`
266
+ API has no unified headers / routing-key parameter — handling is
267
+ transport-specific (Kafka headers, AMQP properties, NATS subject
268
+ suffixes, ...). For now the externalizer logs resolved values at
269
+ debug level for visibility; the broker-aware message-construction
270
+ iteration ships in a later release. Wrap the event in a
271
+ transport-specific envelope inside your own code if you need them
272
+ before then.
273
+ - **Real-broker integration tests** (Postgres + Kafka / RabbitMQ via
274
+ testcontainers) are not bundled with this package — the unit and
275
+ module specs use a mock `ClientProxy` and cover the SPI contract
276
+ end-to-end without a live broker. The
277
+ [`externalization-with-fallback`](../../examples/externalization-with-fallback/)
278
+ example demonstrates a real-broker setup via docker-compose with
279
+ the ADR-016 silent-success limitation observable end-to-end.
280
+
281
+ ## Testing
282
+
283
+ The package's own tests use a mock `ClientProxy` directly. To exercise
284
+ the externalizer in your application's tests, register a stub provider
285
+ under your client's token before the module imports:
286
+
287
+ ```typescript
288
+ const moduleRef = await Test.createTestingModule({
289
+ imports: [
290
+ /* ... ClientsModule.register([{ name: 'KAFKA_CLIENT', ... }]) */
291
+ OutboxMicroservicesModule.forRoot({ defaultClient: 'KAFKA_CLIENT' }),
292
+ ],
293
+ })
294
+ .overrideProvider('KAFKA_CLIENT')
295
+ .useValue({ emit: jest.fn(() => of(undefined)) })
296
+ .compile();
297
+ ```
298
+
299
+ ## Worked examples
300
+
301
+ - [`externalization-kafka`](../../examples/externalization-kafka) — single DataSource + single Kafka broker, the canonical baseline.
302
+ - [`externalization-multi-broker`](../../examples/externalization-multi-broker) — Kafka + RabbitMQ + Redis pub/sub routed per event via `@Externalized({ client })`.
303
+ - [`externalization-multi-datasource`](../../examples/externalization-multi-datasource) — two physical Postgres × two `ClientProxy` registrations on a single broker.
304
+ - [`externalization-with-fallback`](../../examples/externalization-with-fallback) — ADR-016 silent-success demo + the three production mitigation patterns + `FailedEventPublications.resubmit` recovery flow.
305
+ - [`e-commerce-orders`](../../examples/e-commerce-orders) — flagship application; externalization is the terminal step of the order saga.
306
+
307
+ Full catalogue: [examples/README.md](../../examples/README.md).
308
+
309
+ ## License
310
+
311
+ MIT — see [LICENSE](../../LICENSE).
@@ -0,0 +1,37 @@
1
+ import { type OnApplicationBootstrap } from '@nestjs/common';
2
+ import { ModuleRef } from '@nestjs/core';
3
+ import { type EventExternalizer, type ExternalizationMetadata } from '@nestjs-transactional/outbox';
4
+ import { type OutboxMicroservicesOptions } from '../types/options';
5
+ /**
6
+ * {@link EventExternalizer} implementation backed by `@nestjs/microservices`
7
+ * `ClientProxy`. Routes externalized events to whichever transport
8
+ * the configured `ClientProxy` was registered with — Kafka, RabbitMQ,
9
+ * NATS, JMS, gRPC, or any custom transport.
10
+ *
11
+ * Per DD-017 this externalizer does NOT register clients itself;
12
+ * users register them via standard `ClientsModule.register()` /
13
+ * `ClientsModule.registerAsync()` and pass the token through
14
+ * `OutboxMicroservicesModule.forRoot({ defaultClient })`. Per-event
15
+ * overrides via `@Externalized({ client })` resolve through the same
16
+ * `ModuleRef.get(token, { strict: false })` lookup.
17
+ *
18
+ * **Headers / routingKey limitation (Phase 11.3):** the
19
+ * `@nestjs/microservices` `ClientProxy.emit` API has no unified
20
+ * headers / routing-key parameter — handling is transport-specific
21
+ * (Kafka headers, AMQP properties, NATS subject suffixes, ...). For
22
+ * the first version we log resolved headers and routing keys at debug
23
+ * level for visibility but do not apply them to the wire payload —
24
+ * users that need them can wrap the event in a transport-specific
25
+ * envelope inside their own code or wait for the broker-aware message
26
+ * construction iteration.
27
+ */
28
+ export declare class MicroservicesEventExternalizer implements EventExternalizer, OnApplicationBootstrap {
29
+ private readonly moduleRef;
30
+ private readonly options;
31
+ private readonly logger;
32
+ constructor(moduleRef: ModuleRef, options: OutboxMicroservicesOptions);
33
+ onApplicationBootstrap(): void;
34
+ externalize(event: unknown, metadata: ExternalizationMetadata): Promise<void>;
35
+ private resolveClient;
36
+ }
37
+ //# sourceMappingURL=microservices-event-externalizer.d.ts.map
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var MicroservicesEventExternalizer_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.MicroservicesEventExternalizer = void 0;
17
+ const common_1 = require("@nestjs/common");
18
+ const core_1 = require("@nestjs/core");
19
+ const outbox_1 = require("@nestjs-transactional/outbox");
20
+ const rxjs_1 = require("rxjs");
21
+ const options_1 = require("../types/options");
22
+ /**
23
+ * {@link EventExternalizer} implementation backed by `@nestjs/microservices`
24
+ * `ClientProxy`. Routes externalized events to whichever transport
25
+ * the configured `ClientProxy` was registered with — Kafka, RabbitMQ,
26
+ * NATS, JMS, gRPC, or any custom transport.
27
+ *
28
+ * Per DD-017 this externalizer does NOT register clients itself;
29
+ * users register them via standard `ClientsModule.register()` /
30
+ * `ClientsModule.registerAsync()` and pass the token through
31
+ * `OutboxMicroservicesModule.forRoot({ defaultClient })`. Per-event
32
+ * overrides via `@Externalized({ client })` resolve through the same
33
+ * `ModuleRef.get(token, { strict: false })` lookup.
34
+ *
35
+ * **Headers / routingKey limitation (Phase 11.3):** the
36
+ * `@nestjs/microservices` `ClientProxy.emit` API has no unified
37
+ * headers / routing-key parameter — handling is transport-specific
38
+ * (Kafka headers, AMQP properties, NATS subject suffixes, ...). For
39
+ * the first version we log resolved headers and routing keys at debug
40
+ * level for visibility but do not apply them to the wire payload —
41
+ * users that need them can wrap the event in a transport-specific
42
+ * envelope inside their own code or wait for the broker-aware message
43
+ * construction iteration.
44
+ */
45
+ let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class MicroservicesEventExternalizer {
46
+ moduleRef;
47
+ options;
48
+ logger = new common_1.Logger(MicroservicesEventExternalizer_1.name);
49
+ constructor(moduleRef, options) {
50
+ this.moduleRef = moduleRef;
51
+ this.options = options;
52
+ }
53
+ onApplicationBootstrap() {
54
+ if (this.options.validateOnBootstrap === false) {
55
+ this.logger.log('Bootstrap validation disabled — defaultClient will be resolved on first externalize() call');
56
+ return;
57
+ }
58
+ if (this.options.defaultClient === undefined) {
59
+ this.logger.log('No defaultClient configured — every @Externalized event must specify its own client');
60
+ return;
61
+ }
62
+ try {
63
+ this.resolveClient(this.options.defaultClient);
64
+ this.logger.log(`Externalization configured with default client: ${formatToken(this.options.defaultClient)}`);
65
+ }
66
+ catch (err) {
67
+ const cause = err instanceof Error ? err.message : String(err);
68
+ throw new Error(`OutboxMicroservicesModule: defaultClient '${formatToken(this.options.defaultClient)}' ` +
69
+ `is not registered in the DI container. Register the ClientProxy via ` +
70
+ `ClientsModule.register() / ClientsModule.registerAsync(), or pass ` +
71
+ `validateOnBootstrap: false to defer resolution to the first event. ` +
72
+ `Original error: ${cause}`);
73
+ }
74
+ }
75
+ async externalize(event, metadata) {
76
+ const clientToken = metadata.client ?? this.options.defaultClient;
77
+ if (clientToken === undefined) {
78
+ throw new outbox_1.ExternalizationError(`No ClientProxy specified for event ${metadata.eventType}. ` +
79
+ `Set defaultClient on OutboxMicroservicesModule.forRoot() or pass ` +
80
+ `'client' in the @Externalized() decorator options.`, metadata.eventType, metadata.target);
81
+ }
82
+ let client;
83
+ try {
84
+ client = this.resolveClient(clientToken);
85
+ }
86
+ catch (err) {
87
+ const cause = err instanceof Error ? err : undefined;
88
+ const causeMessage = err instanceof Error ? err.message : String(err);
89
+ throw new outbox_1.ExternalizationError(`ClientProxy '${formatToken(clientToken)}' not found in the DI container. ` +
90
+ `Ensure it is registered via ClientsModule.register() / registerAsync(). ` +
91
+ `Original error: ${causeMessage}`, metadata.eventType, metadata.target, cause);
92
+ }
93
+ if (metadata.headers !== undefined || metadata.routingKey !== undefined) {
94
+ // Phase 11.3 limitation — ClientProxy.emit has no unified
95
+ // headers / routing-key parameter. Logged for visibility; the
96
+ // broker-aware message construction iteration will route them
97
+ // through transport-specific envelopes.
98
+ this.logger.debug(`${metadata.eventType}: headers/routingKey are not applied to the wire payload in this version (Phase 11.3 limitation): ${JSON.stringify({ headers: metadata.headers, routingKey: metadata.routingKey })}`);
99
+ }
100
+ try {
101
+ await (0, rxjs_1.firstValueFrom)(client.emit(metadata.target, event));
102
+ this.logger.debug(`Externalized ${metadata.eventType} → ${metadata.target} (client: ${formatToken(clientToken)})`);
103
+ }
104
+ catch (err) {
105
+ const cause = err instanceof Error ? err : undefined;
106
+ const causeMessage = err instanceof Error ? err.message : String(err);
107
+ throw new outbox_1.ExternalizationError(`Failed to publish ${metadata.eventType} to ${metadata.target}: ${causeMessage}`, metadata.eventType, metadata.target, cause);
108
+ }
109
+ }
110
+ resolveClient(token) {
111
+ const proxy = this.moduleRef.get(token, { strict: false });
112
+ if (proxy === null || proxy === undefined) {
113
+ throw new Error('Resolved provider is null/undefined');
114
+ }
115
+ return proxy;
116
+ }
117
+ };
118
+ exports.MicroservicesEventExternalizer = MicroservicesEventExternalizer;
119
+ exports.MicroservicesEventExternalizer = MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = __decorate([
120
+ (0, common_1.Injectable)(),
121
+ __param(1, (0, common_1.Inject)(options_1.OUTBOX_MICROSERVICES_OPTIONS)),
122
+ __metadata("design:paramtypes", [core_1.ModuleRef, Object])
123
+ ], MicroservicesEventExternalizer);
124
+ function formatToken(token) {
125
+ if (typeof token === 'symbol') {
126
+ return token.toString();
127
+ }
128
+ if (typeof token === 'function') {
129
+ return token.name || '<anonymous function token>';
130
+ }
131
+ return String(token);
132
+ }
133
+ //# sourceMappingURL=microservices-event-externalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microservices-event-externalizer.js","sourceRoot":"","sources":["../../src/externalizer/microservices-event-externalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { MicroservicesEventExternalizer } from './externalizer/microservices-event-externalizer';
2
+ export { OutboxMicroservicesModule, type OutboxMicroservicesAsyncOptions, } from './module/outbox-microservices.module';
3
+ export { OUTBOX_MICROSERVICES_OPTIONS, type OutboxMicroservicesOptions, } from './types/options';
4
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OUTBOX_MICROSERVICES_OPTIONS = exports.OutboxMicroservicesModule = exports.MicroservicesEventExternalizer = void 0;
4
+ var microservices_event_externalizer_1 = require("./externalizer/microservices-event-externalizer");
5
+ Object.defineProperty(exports, "MicroservicesEventExternalizer", { enumerable: true, get: function () { return microservices_event_externalizer_1.MicroservicesEventExternalizer; } });
6
+ var outbox_microservices_module_1 = require("./module/outbox-microservices.module");
7
+ Object.defineProperty(exports, "OutboxMicroservicesModule", { enumerable: true, get: function () { return outbox_microservices_module_1.OutboxMicroservicesModule; } });
8
+ var options_1 = require("./types/options");
9
+ Object.defineProperty(exports, "OUTBOX_MICROSERVICES_OPTIONS", { enumerable: true, get: function () { return options_1.OUTBOX_MICROSERVICES_OPTIONS; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oGAAiG;AAAxF,kJAAA,8BAA8B,OAAA;AACvC,oFAG8C;AAF5C,wIAAA,yBAAyB,OAAA;AAG3B,2CAGyB;AAFvB,uHAAA,4BAA4B,OAAA"}
@@ -0,0 +1,60 @@
1
+ import { type DynamicModule, type InjectionToken, type ModuleMetadata } from '@nestjs/common';
2
+ import { type OutboxMicroservicesOptions } from '../types/options';
3
+ /**
4
+ * Async-options shape for {@link OutboxMicroservicesModule.forRootAsync}.
5
+ * `imports` follows NestJS convention so the factory can pull values
6
+ * from a `ConfigModule` (or similar) without leaking the module
7
+ * reference into the factory signature.
8
+ */
9
+ export interface OutboxMicroservicesAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
10
+ readonly useFactory: (...args: never[]) => Promise<OutboxMicroservicesOptions> | OutboxMicroservicesOptions;
11
+ readonly inject?: readonly InjectionToken[];
12
+ }
13
+ /**
14
+ * NestJS module that wires the
15
+ * {@link MicroservicesEventExternalizer} as the
16
+ * `EventExternalizer` for `outbox`. Reuses the user's existing
17
+ * `@nestjs/microservices` `ClientsModule` registration (DD-017) — the
18
+ * package does NOT register clients itself.
19
+ *
20
+ * Typical wiring:
21
+ * ```ts
22
+ * @Module({
23
+ * imports: [
24
+ * ClientsModule.register([
25
+ * { name: 'KAFKA_CLIENT', transport: Transport.KAFKA, options: { ... } },
26
+ * ]),
27
+ * OutboxModule.forRoot({}),
28
+ * OutboxModule.forFeature([OrderPlacedEvent]),
29
+ * OutboxMicroservicesModule.forRoot({ defaultClient: 'KAFKA_CLIENT' }),
30
+ * ],
31
+ * })
32
+ * export class AppModule {}
33
+ * ```
34
+ *
35
+ * The module binds {@link EVENT_EXTERNALIZER} via `useExisting` so
36
+ * `OutboxModule`'s `EventPublicationProcessor` picks the externalizer
37
+ * up through its `@Optional()` injection (DD-018). Both the SPI
38
+ * binding and the concrete class are exported so consumers can inject
39
+ * either.
40
+ *
41
+ * **Multi-dataSource setups**: a single externalizer covers every
42
+ * dataSource. Per-broker routing — when different events should land
43
+ * on different transports — happens via the per-event
44
+ * `@Externalized({ client })` parameter (Phase 11.3), not via a
45
+ * dataSource-keyed externalizer Map. See `outbox` README for the
46
+ * multi-`OutboxModule.forRoot()` pattern (ADR-019); each per-DS
47
+ * processor injects this same externalizer via `EVENT_EXTERNALIZER`.
48
+ *
49
+ * The module is registered as `@Global()` (since Phase 14.6) so the
50
+ * `EVENT_EXTERNALIZER` binding is visible to `OutboxModule`'s
51
+ * sibling-imported per-DS processors without an explicit import
52
+ * chain. Pre-Phase-14.6 the module was non-global, which silently
53
+ * broke the documented usage pattern in multi-module trees — fixed
54
+ * in Phase 14.6 verification work.
55
+ */
56
+ export declare class OutboxMicroservicesModule {
57
+ static forRoot(options?: OutboxMicroservicesOptions): DynamicModule;
58
+ static forRootAsync(options: OutboxMicroservicesAsyncOptions): DynamicModule;
59
+ }
60
+ //# sourceMappingURL=outbox-microservices.module.d.ts.map
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var OutboxMicroservicesModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.OutboxMicroservicesModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const outbox_1 = require("@nestjs-transactional/outbox");
13
+ const microservices_event_externalizer_1 = require("../externalizer/microservices-event-externalizer");
14
+ const options_1 = require("../types/options");
15
+ /**
16
+ * NestJS module that wires the
17
+ * {@link MicroservicesEventExternalizer} as the
18
+ * `EventExternalizer` for `outbox`. Reuses the user's existing
19
+ * `@nestjs/microservices` `ClientsModule` registration (DD-017) — the
20
+ * package does NOT register clients itself.
21
+ *
22
+ * Typical wiring:
23
+ * ```ts
24
+ * @Module({
25
+ * imports: [
26
+ * ClientsModule.register([
27
+ * { name: 'KAFKA_CLIENT', transport: Transport.KAFKA, options: { ... } },
28
+ * ]),
29
+ * OutboxModule.forRoot({}),
30
+ * OutboxModule.forFeature([OrderPlacedEvent]),
31
+ * OutboxMicroservicesModule.forRoot({ defaultClient: 'KAFKA_CLIENT' }),
32
+ * ],
33
+ * })
34
+ * export class AppModule {}
35
+ * ```
36
+ *
37
+ * The module binds {@link EVENT_EXTERNALIZER} via `useExisting` so
38
+ * `OutboxModule`'s `EventPublicationProcessor` picks the externalizer
39
+ * up through its `@Optional()` injection (DD-018). Both the SPI
40
+ * binding and the concrete class are exported so consumers can inject
41
+ * either.
42
+ *
43
+ * **Multi-dataSource setups**: a single externalizer covers every
44
+ * dataSource. Per-broker routing — when different events should land
45
+ * on different transports — happens via the per-event
46
+ * `@Externalized({ client })` parameter (Phase 11.3), not via a
47
+ * dataSource-keyed externalizer Map. See `outbox` README for the
48
+ * multi-`OutboxModule.forRoot()` pattern (ADR-019); each per-DS
49
+ * processor injects this same externalizer via `EVENT_EXTERNALIZER`.
50
+ *
51
+ * The module is registered as `@Global()` (since Phase 14.6) so the
52
+ * `EVENT_EXTERNALIZER` binding is visible to `OutboxModule`'s
53
+ * sibling-imported per-DS processors without an explicit import
54
+ * chain. Pre-Phase-14.6 the module was non-global, which silently
55
+ * broke the documented usage pattern in multi-module trees — fixed
56
+ * in Phase 14.6 verification work.
57
+ */
58
+ let OutboxMicroservicesModule = OutboxMicroservicesModule_1 = class OutboxMicroservicesModule {
59
+ static forRoot(options = {}) {
60
+ const providers = [
61
+ { provide: options_1.OUTBOX_MICROSERVICES_OPTIONS, useValue: options },
62
+ microservices_event_externalizer_1.MicroservicesEventExternalizer,
63
+ { provide: outbox_1.EVENT_EXTERNALIZER, useExisting: microservices_event_externalizer_1.MicroservicesEventExternalizer },
64
+ ];
65
+ return {
66
+ module: OutboxMicroservicesModule_1,
67
+ global: true,
68
+ providers,
69
+ exports: [outbox_1.EVENT_EXTERNALIZER, microservices_event_externalizer_1.MicroservicesEventExternalizer],
70
+ };
71
+ }
72
+ static forRootAsync(options) {
73
+ const providers = [
74
+ {
75
+ provide: options_1.OUTBOX_MICROSERVICES_OPTIONS,
76
+ useFactory: options.useFactory,
77
+ inject: options.inject ? [...options.inject] : undefined,
78
+ },
79
+ microservices_event_externalizer_1.MicroservicesEventExternalizer,
80
+ { provide: outbox_1.EVENT_EXTERNALIZER, useExisting: microservices_event_externalizer_1.MicroservicesEventExternalizer },
81
+ ];
82
+ return {
83
+ module: OutboxMicroservicesModule_1,
84
+ global: true,
85
+ imports: options.imports ?? [],
86
+ providers,
87
+ exports: [outbox_1.EVENT_EXTERNALIZER, microservices_event_externalizer_1.MicroservicesEventExternalizer],
88
+ };
89
+ }
90
+ };
91
+ exports.OutboxMicroservicesModule = OutboxMicroservicesModule;
92
+ exports.OutboxMicroservicesModule = OutboxMicroservicesModule = OutboxMicroservicesModule_1 = __decorate([
93
+ (0, common_1.Module)({})
94
+ ], OutboxMicroservicesModule);
95
+ //# sourceMappingURL=outbox-microservices.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-microservices.module.js","sourceRoot":"","sources":["../../src/module/outbox-microservices.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAMwB;AACxB,yDAAkE;AAElE,uGAAkG;AAClG,8CAG0B;AAe1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEI,IAAM,yBAAyB,iCAA/B,MAAM,yBAAyB;IACpC,MAAM,CAAC,OAAO,CAAC,UAAsC,EAAE;QACrD,MAAM,SAAS,GAAe;YAC5B,EAAE,OAAO,EAAE,sCAA4B,EAAE,QAAQ,EAAE,OAAO,EAAE;YAC5D,iEAA8B;YAC9B,EAAE,OAAO,EAAE,2BAAkB,EAAE,WAAW,EAAE,iEAA8B,EAAE;SAC7E,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,2BAAyB;YACjC,MAAM,EAAE,IAAI;YACZ,SAAS;YACT,OAAO,EAAE,CAAC,2BAAkB,EAAE,iEAA8B,CAAC;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAwC;QAC1D,MAAM,SAAS,GAAe;YAC5B;gBACE,OAAO,EAAE,sCAA4B;gBACrC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;aACzD;YACD,iEAA8B;YAC9B,EAAE,OAAO,EAAE,2BAAkB,EAAE,WAAW,EAAE,iEAA8B,EAAE;SAC7E,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,2BAAyB;YACjC,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS;YACT,OAAO,EAAE,CAAC,2BAAkB,EAAE,iEAA8B,CAAC;SAC9D,CAAC;IACJ,CAAC;CACF,CAAA;AAnCY,8DAAyB;oCAAzB,yBAAyB;IADrC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,yBAAyB,CAmCrC"}
@@ -0,0 +1,40 @@
1
+ import { type InjectionToken } from '@nestjs/common';
2
+ /**
3
+ * Configuration accepted by
4
+ * {@link OutboxMicroservicesModule.forRoot} (and the resolved factory
5
+ * result of `forRootAsync`). Per DD-017 the package does NOT register
6
+ * `ClientProxy` instances itself — register them via the standard
7
+ * `ClientsModule.register()` / `ClientsModule.registerAsync()` from
8
+ * `@nestjs/microservices` and pass the token here.
9
+ */
10
+ export interface OutboxMicroservicesOptions {
11
+ /**
12
+ * DI token of the default `ClientProxy` used when an `@Externalized`
13
+ * mapping does not specify its own `client` override. Resolved via
14
+ * `ModuleRef.get(token, { strict: false })` so the proxy can live in
15
+ * any module (including a globally-imported `ClientsModule`).
16
+ *
17
+ * If omitted, every `@Externalized` event must carry a `client` of
18
+ * its own — otherwise the externalizer rejects the publication and
19
+ * the row is recorded as `FAILED` (DD-019).
20
+ */
21
+ readonly defaultClient?: InjectionToken;
22
+ /**
23
+ * When `true` (default), the externalizer's `OnApplicationBootstrap`
24
+ * hook resolves `defaultClient` immediately and fails the bootstrap
25
+ * if the binding is missing — surfacing a misconfiguration before
26
+ * the first event is processed. Set `false` to defer resolution
27
+ * until the first call to `externalize()` (useful when the
28
+ * `ClientProxy` registration is itself wired by an asynchronous
29
+ * factory that has not finished by the time bootstrap runs).
30
+ */
31
+ readonly validateOnBootstrap?: boolean;
32
+ }
33
+ /**
34
+ * DI token for the resolved {@link OutboxMicroservicesOptions} object.
35
+ * Internal — consumers configure the module via
36
+ * `OutboxMicroservicesModule.forRoot()` rather than injecting the
37
+ * token directly.
38
+ */
39
+ export declare const OUTBOX_MICROSERVICES_OPTIONS: unique symbol;
40
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OUTBOX_MICROSERVICES_OPTIONS = void 0;
4
+ /**
5
+ * DI token for the resolved {@link OutboxMicroservicesOptions} object.
6
+ * Internal — consumers configure the module via
7
+ * `OutboxMicroservicesModule.forRoot()` rather than injecting the
8
+ * token directly.
9
+ */
10
+ exports.OUTBOX_MICROSERVICES_OPTIONS = Symbol('OUTBOX_MICROSERVICES_OPTIONS');
11
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":";;;AAmCA;;;;;GAKG;AACU,QAAA,4BAA4B,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@nestjs-transactional/outbox-microservices",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "Event externalization for @nestjs-transactional/outbox via @nestjs/microservices ClientProxy — Spring Modulith @Externalized parity",
5
+ "license": "MIT",
6
+ "author": "Igor Golovanov",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/igorgolovanov/nestjs-transactional.git",
10
+ "directory": "packages/outbox-microservices"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/igorgolovanov/nestjs-transactional/issues"
14
+ },
15
+ "homepage": "https://github.com/igorgolovanov/nestjs-transactional/tree/main/packages/outbox-microservices#readme",
16
+ "keywords": [
17
+ "nestjs",
18
+ "microservices",
19
+ "outbox",
20
+ "event-publication-registry",
21
+ "event-externalization",
22
+ "spring-modulith",
23
+ "kafka",
24
+ "rabbitmq",
25
+ "nats"
26
+ ],
27
+ "engines": {
28
+ "node": ">=22.11.0"
29
+ },
30
+ "main": "dist/index.js",
31
+ "types": "dist/index.d.ts",
32
+ "files": [
33
+ "dist/**/*.js",
34
+ "dist/**/*.d.ts",
35
+ "dist/**/*.js.map",
36
+ "!dist/**/*.spec.*"
37
+ ],
38
+ "exports": {
39
+ ".": {
40
+ "types": "./dist/index.d.ts",
41
+ "default": "./dist/index.js"
42
+ }
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "provenance": true
47
+ },
48
+ "peerDependencies": {
49
+ "@nestjs/common": "^10.0.0 || ^11.0.0",
50
+ "@nestjs/core": "^10.0.0 || ^11.0.0",
51
+ "@nestjs/microservices": "^10.0.0 || ^11.0.0",
52
+ "reflect-metadata": "^0.1.13 || ^0.2.0",
53
+ "rxjs": "^7.0.0",
54
+ "@nestjs-transactional/outbox": "^1.0.0-alpha.0"
55
+ },
56
+ "devDependencies": {
57
+ "@nestjs/common": "^11.0.0",
58
+ "@nestjs/core": "^11.0.0",
59
+ "@nestjs/microservices": "^11.0.0",
60
+ "@nestjs/testing": "^11.0.0",
61
+ "@types/jest": "^29.5.14",
62
+ "jest": "^29.7.0",
63
+ "reflect-metadata": "^0.2.2",
64
+ "rxjs": "^7.8.1",
65
+ "ts-jest": "^29.2.5",
66
+ "typescript": "^5.5.4",
67
+ "@nestjs-transactional/core": "1.0.0-alpha.0",
68
+ "@nestjs-transactional/outbox": "1.0.0-alpha.0"
69
+ },
70
+ "scripts": {
71
+ "build": "tsc -p tsconfig.build.json",
72
+ "clean": "rimraf dist *.tsbuildinfo coverage",
73
+ "test": "jest",
74
+ "test:watch": "jest --watch",
75
+ "test:cov": "jest --coverage",
76
+ "type-check": "tsc --noEmit",
77
+ "lint": "eslint \"src/**/*.ts\""
78
+ }
79
+ }