@rolandsall24/nest-mediator 0.6.0 → 0.7.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.
Files changed (73) hide show
  1. package/README.md +354 -56
  2. package/dist/lib/decorators/event-criticality.decorator.d.ts +61 -0
  3. package/dist/lib/decorators/event-criticality.decorator.d.ts.map +1 -0
  4. package/dist/lib/decorators/event-criticality.decorator.js +71 -0
  5. package/dist/lib/decorators/event-criticality.decorator.js.map +1 -0
  6. package/dist/lib/decorators/event-handler.decorator.d.ts +21 -0
  7. package/dist/lib/decorators/event-handler.decorator.d.ts.map +1 -0
  8. package/dist/lib/decorators/event-handler.decorator.js +27 -0
  9. package/dist/lib/decorators/event-handler.decorator.js.map +1 -0
  10. package/dist/lib/decorators/index.d.ts +2 -0
  11. package/dist/lib/decorators/index.d.ts.map +1 -1
  12. package/dist/lib/decorators/index.js +2 -0
  13. package/dist/lib/decorators/index.js.map +1 -1
  14. package/dist/lib/interfaces/command-bus.interface.d.ts +25 -0
  15. package/dist/lib/interfaces/command-bus.interface.d.ts.map +1 -0
  16. package/dist/lib/interfaces/command-bus.interface.js +3 -0
  17. package/dist/lib/interfaces/command-bus.interface.js.map +1 -0
  18. package/dist/lib/interfaces/event-bus.interface.d.ts +35 -0
  19. package/dist/lib/interfaces/event-bus.interface.d.ts.map +1 -0
  20. package/dist/lib/interfaces/event-bus.interface.js +3 -0
  21. package/dist/lib/interfaces/event-bus.interface.js.map +1 -0
  22. package/dist/lib/interfaces/event-consumer.interface.d.ts +64 -0
  23. package/dist/lib/interfaces/event-consumer.interface.d.ts.map +1 -0
  24. package/dist/lib/interfaces/event-consumer.interface.js +3 -0
  25. package/dist/lib/interfaces/event-consumer.interface.js.map +1 -0
  26. package/dist/lib/interfaces/event-criticality.interface.d.ts +28 -0
  27. package/dist/lib/interfaces/event-criticality.interface.d.ts.map +1 -0
  28. package/dist/lib/interfaces/event-criticality.interface.js +25 -0
  29. package/dist/lib/interfaces/event-criticality.interface.js.map +1 -0
  30. package/dist/lib/interfaces/event-handler.interface.d.ts +24 -0
  31. package/dist/lib/interfaces/event-handler.interface.d.ts.map +1 -0
  32. package/dist/lib/interfaces/event-handler.interface.js +3 -0
  33. package/dist/lib/interfaces/event-handler.interface.js.map +1 -0
  34. package/dist/lib/interfaces/event.interface.d.ts +30 -0
  35. package/dist/lib/interfaces/event.interface.d.ts.map +1 -0
  36. package/dist/lib/interfaces/event.interface.js +3 -0
  37. package/dist/lib/interfaces/event.interface.js.map +1 -0
  38. package/dist/lib/interfaces/index.d.ts +7 -0
  39. package/dist/lib/interfaces/index.d.ts.map +1 -1
  40. package/dist/lib/interfaces/index.js +7 -0
  41. package/dist/lib/interfaces/index.js.map +1 -1
  42. package/dist/lib/interfaces/mediator.interface.d.ts +14 -0
  43. package/dist/lib/interfaces/mediator.interface.d.ts.map +1 -0
  44. package/dist/lib/interfaces/mediator.interface.js +3 -0
  45. package/dist/lib/interfaces/mediator.interface.js.map +1 -0
  46. package/dist/lib/interfaces/query-bus.interface.d.ts +26 -0
  47. package/dist/lib/interfaces/query-bus.interface.d.ts.map +1 -0
  48. package/dist/lib/interfaces/query-bus.interface.js +3 -0
  49. package/dist/lib/interfaces/query-bus.interface.js.map +1 -0
  50. package/dist/lib/nest-mediator.module.d.ts.map +1 -1
  51. package/dist/lib/nest-mediator.module.js +19 -0
  52. package/dist/lib/nest-mediator.module.js.map +1 -1
  53. package/dist/lib/services/command.bus.d.ts +30 -0
  54. package/dist/lib/services/command.bus.d.ts.map +1 -0
  55. package/dist/lib/services/command.bus.js +69 -0
  56. package/dist/lib/services/command.bus.js.map +1 -0
  57. package/dist/lib/services/event.bus.d.ts +61 -0
  58. package/dist/lib/services/event.bus.d.ts.map +1 -0
  59. package/dist/lib/services/event.bus.js +176 -0
  60. package/dist/lib/services/event.bus.js.map +1 -0
  61. package/dist/lib/services/mediator.bus.d.ts +48 -30
  62. package/dist/lib/services/mediator.bus.d.ts.map +1 -1
  63. package/dist/lib/services/mediator.bus.js +58 -101
  64. package/dist/lib/services/mediator.bus.js.map +1 -1
  65. package/dist/lib/services/pipeline.orchestrator.d.ts +46 -0
  66. package/dist/lib/services/pipeline.orchestrator.d.ts.map +1 -0
  67. package/dist/lib/services/pipeline.orchestrator.js +87 -0
  68. package/dist/lib/services/pipeline.orchestrator.js.map +1 -0
  69. package/dist/lib/services/query.bus.d.ts +31 -0
  70. package/dist/lib/services/query.bus.d.ts.map +1 -0
  71. package/dist/lib/services/query.bus.js +68 -0
  72. package/dist/lib/services/query.bus.js.map +1 -0
  73. package/package.json +1 -1
package/README.md CHANGED
@@ -4,12 +4,13 @@ A lightweight CQRS (Command Query Responsibility Segregation) mediator pattern i
4
4
 
5
5
  ## Features
6
6
 
7
- - Clean separation between Commands and Queries
7
+ - Clean separation between Commands, Queries, and Events
8
8
  - Type-safe handlers with TypeScript
9
9
  - Decorator-based handler registration
10
10
  - Automatic handler discovery and registration
11
11
  - Pipeline Behaviors for cross-cutting concerns (logging, validation, etc.)
12
12
  - Type-Specific Behaviors - behaviors that only apply to specific request types (v0.6.0+)
13
+ - **Domain Events with Critical/Non-Critical consumers (v0.7.0+)**
13
14
  - Built-in behaviors: Logging, Validation, Exception Handling, Performance Tracking
14
15
  - Built on top of NestJS dependency injection
15
16
  - Zero runtime dependencies beyond NestJS
@@ -33,6 +34,32 @@ This library requires TypeScript decorators to be enabled. Add the following to
33
34
  }
34
35
  ```
35
36
 
37
+ ## Upgrading to v0.7.0
38
+
39
+ Version 0.7.0 introduces **Domain Events** with Critical and Non-Critical consumer support.
40
+
41
+ ### What's New
42
+
43
+ - `IEvent` interface for domain events
44
+ - `IEventConsumer<TEvent>` interface for event consumers
45
+ - `ICriticalEventConsumer<TEvent>` interface for critical consumers with optional compensation
46
+ - `@EventHandler(EventClass)` decorator to register consumers
47
+ - `@Critical({ order: n })` decorator for critical consumers (run sequentially)
48
+ - `@NonCritical()` decorator for non-critical consumers (fire-and-forget)
49
+ - `mediatorBus.publish(event)` method to publish events
50
+ - `EventCriticality` enum (`CRITICAL`, `NON_CRITICAL`)
51
+ - **Saga-style compensation**: Critical consumers can define a `compensate()` method that runs in reverse order when a subsequent consumer fails
52
+ - Internal architecture refactoring (MediatorBus now delegates to CommandBus, QueryBus, EventBus)
53
+
54
+ ### Backward Compatibility
55
+
56
+ - **`IEventHandler` renamed to `IEventConsumer`**: `IEventHandler` is now a deprecated type alias. Update your imports:
57
+
58
+ - **MediatorBus API unchanged**: The `send()`, `query()`, and `publish()` methods work exactly as before
59
+ - **No breaking changes**: Existing command/query code continues to work without modifications
60
+
61
+ ---
62
+
36
63
  ## Upgrading to v0.6.0
37
64
 
38
65
  Version 0.6.0 introduces **Type-Specific Pipeline Behaviors** - behaviors that only apply to specific request types.
@@ -82,58 +109,6 @@ Existing behaviors without `@Handle()` on the method continue to work exactly as
82
109
 
83
110
  ---
84
111
 
85
- ## Upgrading to v0.5.0
86
-
87
- Version 0.5.0 introduces **Pipeline Behaviors** while maintaining backward compatibility. Existing code using `NestMediatorModule.forRoot()` will continue to work without changes.
88
-
89
- ### What's New
90
-
91
- - Pipeline behaviors for cross-cutting concerns (logging, validation, etc.)
92
- - Built-in behaviors: `LoggingBehavior`, `ValidationBehavior`, `ExceptionHandlingBehavior`, `PerformanceBehavior`
93
- - New `forRoot()` method for enabling behaviors
94
- - Custom `HandlerNotFoundException` for better error handling
95
-
96
- ### Breaking Change Notice
97
-
98
- The `send()` and `query()` methods now throw `HandlerNotFoundException` instead of a generic `Error` when no handler is registered. This is **backward compatible** since `HandlerNotFoundException` extends `Error`, but you may want to update your error handling for more specific catches:
99
-
100
- ```typescript
101
- // Before (still works)
102
- try {
103
- await mediator.send(command);
104
- } catch (error) {
105
- if (error instanceof Error) {
106
- console.log(error.message); // Works as before
107
- }
108
- }
109
-
110
- // After (optional - for more specific handling)
111
- import { HandlerNotFoundException } from '@rolandsall24/nest-mediator';
112
-
113
- try {
114
- await mediator.send(command);
115
- } catch (error) {
116
- if (error instanceof HandlerNotFoundException) {
117
- console.log(`No handler for: ${error.requestName}`);
118
- }
119
- }
120
- ```
121
-
122
- ### No Migration Required
123
-
124
- If you're using `NestMediatorModule.forRoot()`, no changes are needed. Pipeline behaviors are **opt-in** via `forRoot()`:
125
-
126
- ```typescript
127
- // Existing code - works exactly as before (no behaviors)
128
- NestMediatorModule.forRoot()
129
-
130
- // New - opt-in to behaviors
131
- NestMediatorModule.forRoot({
132
- enableLogging: true,
133
- enableValidation: true,
134
- })
135
- ```
136
-
137
112
  ## Quick Start
138
113
 
139
114
  ### 1. Import the Module
@@ -337,6 +312,236 @@ export class UserController {
337
312
  }
338
313
  ```
339
314
 
315
+ ### Domain Events
316
+
317
+ Domain events notify other parts of the system when something important happens. They support two consumer types:
318
+
319
+ - **Critical consumers**: Run sequentially in order. Must succeed for the operation to complete.
320
+ - **Non-critical consumers**: Run in parallel after critical consumers complete. Fire-and-forget.
321
+
322
+ #### 1. Define an Event
323
+
324
+ ```typescript
325
+ import { IEvent } from '@rolandsall24/nest-mediator';
326
+
327
+ export class OrderPlacedEvent implements IEvent {
328
+ constructor(
329
+ public readonly orderId: string,
330
+ public readonly customerId: string,
331
+ public readonly items: { productId: string; quantity: number }[],
332
+ public readonly total: number,
333
+ ) {}
334
+ }
335
+ ```
336
+
337
+ #### 2. Create Event Consumers
338
+
339
+ **Critical consumer** (must succeed, runs in order):
340
+
341
+ ```typescript
342
+ import { Injectable, Logger } from '@nestjs/common';
343
+ import { EventHandler, IEventConsumer, Critical } from '@rolandsall24/nest-mediator';
344
+ import { OrderPlacedEvent } from './order-placed.event';
345
+
346
+ @Injectable()
347
+ @EventHandler(OrderPlacedEvent)
348
+ @Critical({ order: 1 }) // Runs first among critical consumers
349
+ export class ValidateInventoryConsumer implements IEventConsumer<OrderPlacedEvent> {
350
+ private readonly logger = new Logger(ValidateInventoryConsumer.name);
351
+
352
+ async handle(event: OrderPlacedEvent): Promise<void> {
353
+ this.logger.log(`Validating inventory for order ${event.orderId}`);
354
+ // Validate all items are in stock
355
+ // Throw error if validation fails - stops the event processing
356
+ }
357
+ }
358
+ ```
359
+
360
+ **Critical consumer with compensation** (implements rollback on failure):
361
+
362
+ ```typescript
363
+ import { Injectable, Logger } from '@nestjs/common';
364
+ import { EventHandler, ICriticalEventConsumer, Critical } from '@rolandsall24/nest-mediator';
365
+ import { OrderPlacedEvent } from './order-placed.event';
366
+
367
+ @Injectable()
368
+ @EventHandler(OrderPlacedEvent)
369
+ @Critical({ order: 2 })
370
+ export class ReserveInventoryConsumer implements ICriticalEventConsumer<OrderPlacedEvent> {
371
+ private readonly logger = new Logger(ReserveInventoryConsumer.name);
372
+
373
+ async handle(event: OrderPlacedEvent): Promise<void> {
374
+ this.logger.log(`Reserving inventory for order ${event.orderId}`);
375
+ // Reserve inventory in the database
376
+ }
377
+
378
+ // Called if a SUBSEQUENT critical consumer fails (e.g., ChargePayment at order 4)
379
+ async compensate(event: OrderPlacedEvent): Promise<void> {
380
+ this.logger.warn(`[COMPENSATE] Releasing inventory for order ${event.orderId}`);
381
+ // Release the reserved inventory
382
+ }
383
+ }
384
+ ```
385
+
386
+ **Non-critical consumer** (fire-and-forget):
387
+
388
+ ```typescript
389
+ import { Injectable, Logger } from '@nestjs/common';
390
+ import { EventHandler, IEventConsumer, NonCritical } from '@rolandsall24/nest-mediator';
391
+ import { OrderPlacedEvent } from './order-placed.event';
392
+
393
+ @Injectable()
394
+ @EventHandler(OrderPlacedEvent)
395
+ @NonCritical() // Runs in background after critical consumers
396
+ export class SendOrderConfirmationConsumer implements IEventConsumer<OrderPlacedEvent> {
397
+ private readonly logger = new Logger(SendOrderConfirmationConsumer.name);
398
+
399
+ async handle(event: OrderPlacedEvent): Promise<void> {
400
+ this.logger.log(`Sending confirmation email for order ${event.orderId}`);
401
+ // Send email - failures are logged but don't affect the order
402
+ }
403
+ }
404
+
405
+ // Consumers without @Critical or @NonCritical default to non-critical
406
+ @Injectable()
407
+ @EventHandler(OrderPlacedEvent)
408
+ export class TrackAnalyticsConsumer implements IEventConsumer<OrderPlacedEvent> {
409
+ async handle(event: OrderPlacedEvent): Promise<void> {
410
+ // Track analytics - non-critical by default
411
+ }
412
+ }
413
+ ```
414
+
415
+ #### 3. Publish Events from Command Handlers
416
+
417
+ The recommended pattern is to publish domain events from command handlers after the main operation succeeds:
418
+
419
+ ```typescript
420
+ import { Injectable, Logger } from '@nestjs/common';
421
+ import { CommandHandler, ICommandHandler, MediatorBus } from '@rolandsall24/nest-mediator';
422
+ import { PlaceOrderCommand } from './place-order.command';
423
+ import { OrderPlacedEvent } from '../events/order-placed.event';
424
+
425
+ @Injectable()
426
+ @CommandHandler(PlaceOrderCommand)
427
+ export class PlaceOrderHandler implements ICommandHandler<PlaceOrderCommand> {
428
+ private readonly logger = new Logger(PlaceOrderHandler.name);
429
+
430
+ constructor(private readonly mediatorBus: MediatorBus) {}
431
+
432
+ async execute(command: PlaceOrderCommand): Promise<void> {
433
+ const orderId = `order-${Date.now()}`;
434
+
435
+ // 1. Process the order (validate, save to DB, etc.)
436
+ this.logger.log(`Processing order ${orderId}`);
437
+ await this.processOrder(orderId, command);
438
+
439
+ // 2. Publish domain event after successful processing
440
+ // Critical consumers run sequentially, non-critical run in background
441
+ const result = await this.mediatorBus.publish(
442
+ new OrderPlacedEvent(
443
+ orderId,
444
+ command.customerId,
445
+ command.items,
446
+ command.total,
447
+ ),
448
+ );
449
+
450
+ this.logger.log(
451
+ `Order ${orderId} completed. Critical: ${result.criticalSucceeded}, Non-critical dispatched: ${result.nonCriticalDispatched}`,
452
+ );
453
+ }
454
+
455
+ private async processOrder(orderId: string, command: PlaceOrderCommand): Promise<void> {
456
+ // Order processing logic here
457
+ }
458
+ }
459
+ ```
460
+
461
+ #### 4. Event Execution Flow
462
+
463
+ ```
464
+ publish(OrderPlacedEvent)
465
+
466
+ ├─► Critical Consumers (sequential, awaited)
467
+ │ ├─► ValidateInventoryConsumer (order: 1) ✓
468
+ │ ├─► ReserveInventoryConsumer (order: 2) ✓ [has compensate()]
469
+ │ ├─► CreateOrderRecordConsumer (order: 3) ✓ [has compensate()]
470
+ │ └─► ChargePaymentConsumer (order: 4) ✗ FAILS
471
+
472
+ │ On failure → Run compensations in REVERSE order:
473
+ │ ├─► CreateOrderRecordConsumer.compensate() - deletes order
474
+ │ └─► ReserveInventoryConsumer.compensate() - releases inventory
475
+ │ Then throw original error
476
+
477
+ └─► Non-Critical Consumers (parallel, fire-and-forget)
478
+ ├─► SendOrderConfirmationConsumer
479
+ ├─► NotifyWarehouseConsumer
480
+ └─► TrackAnalyticsConsumer
481
+
482
+ Only runs if ALL critical consumers succeed
483
+ Failures logged but don't affect the result
484
+ ```
485
+
486
+ #### 5. Compensation Pattern (Saga)
487
+
488
+ Critical consumers can implement the `ICriticalEventConsumer` interface with an optional `compensate()` method to support saga-style rollback:
489
+
490
+ ```typescript
491
+ import { ICriticalEventConsumer, EventHandler, Critical } from '@rolandsall24/nest-mediator';
492
+
493
+ @Injectable()
494
+ @EventHandler(OrderPlacedEvent)
495
+ @Critical({ order: 3 })
496
+ export class CreateOrderRecordConsumer implements ICriticalEventConsumer<OrderPlacedEvent> {
497
+ async handle(event: OrderPlacedEvent): Promise<void> {
498
+ // Create order in database
499
+ await this.orderRepository.create({
500
+ id: event.orderId,
501
+ customerId: event.customerId,
502
+ items: event.items,
503
+ total: event.total,
504
+ });
505
+ }
506
+
507
+ async compensate(event: OrderPlacedEvent): Promise<void> {
508
+ // Rollback: delete the order record
509
+ await this.orderRepository.delete(event.orderId);
510
+ }
511
+ }
512
+ ```
513
+
514
+ **Compensation rules:**
515
+ - Only called when a **subsequent** critical consumer fails (not if this consumer fails)
516
+ - Runs in **reverse order** (last succeeded → first succeeded)
517
+ - Receives the same event instance passed to `handle()`
518
+ - Should be **idempotent** - safe to run multiple times
519
+ - Errors in compensations are logged but don't stop other compensations
520
+ - Non-critical consumers don't need compensation (they're fire-and-forget)
521
+
522
+ #### 6. Register Event Consumers
523
+
524
+ Add consumers to your module providers - they're auto-discovered via `@EventHandler`:
525
+
526
+ ```typescript
527
+ @Module({
528
+ imports: [NestMediatorModule.forRoot()],
529
+ providers: [
530
+ // Command handlers
531
+ PlaceOrderHandler,
532
+
533
+ // Event consumers - auto-discovered
534
+ ValidateInventoryConsumer,
535
+ ReserveInventoryConsumer,
536
+ CreateOrderRecordConsumer,
537
+ SendOrderConfirmationConsumer,
538
+ NotifyWarehouseConsumer,
539
+ TrackAnalyticsConsumer,
540
+ ],
541
+ })
542
+ export class AppModule {}
543
+ ```
544
+
340
545
  ## Complete Example
341
546
 
342
547
  Here's a complete example following Domain-Driven Design principles with proper separation of concerns:
@@ -732,6 +937,63 @@ export interface IPipelineBehavior<TRequest = any, TResponse = any> {
732
937
  }
733
938
  ```
734
939
 
940
+ #### `IEvent`
941
+
942
+ Marker interface for domain events.
943
+
944
+ ```typescript
945
+ export interface IEvent {}
946
+ ```
947
+
948
+ #### `IEventConsumer<TEvent>`
949
+
950
+ Interface for event consumers (non-critical or critical without compensation).
951
+
952
+ ```typescript
953
+ export interface IEventConsumer<TEvent extends IEvent> {
954
+ handle(event: TEvent): Promise<void>;
955
+ }
956
+ ```
957
+
958
+ #### `ICriticalEventConsumer<TEvent>`
959
+
960
+ Interface for critical event consumers with optional compensation support (saga pattern).
961
+
962
+ ```typescript
963
+ export interface ICriticalEventConsumer<TEvent extends IEvent> extends IEventConsumer<TEvent> {
964
+ /**
965
+ * Compensate/rollback the work done by handle().
966
+ * Called when a subsequent critical consumer fails.
967
+ * Should be idempotent and derive state from the event.
968
+ */
969
+ compensate?(event: TEvent): Promise<void>;
970
+ }
971
+ ```
972
+
973
+ #### `EventPublishResult`
974
+
975
+ Result returned by `publish()`.
976
+
977
+ ```typescript
978
+ export interface EventPublishResult {
979
+ totalHandlers: number;
980
+ criticalSucceeded: number;
981
+ nonCriticalDispatched: number;
982
+ compensationsRun: number; // Number of compensations executed on failure
983
+ }
984
+ ```
985
+
986
+ #### `EventCriticality`
987
+
988
+ Enum for event consumer criticality.
989
+
990
+ ```typescript
991
+ export enum EventCriticality {
992
+ CRITICAL = 'critical',
993
+ NON_CRITICAL = 'non-critical',
994
+ }
995
+ ```
996
+
735
997
  ### Decorators
736
998
 
737
999
  #### `@CommandHandler(command)`
@@ -799,11 +1061,36 @@ Excludes specific pipeline behaviors from a command or query.
799
1061
  - **Usage**: Apply to command or query classes
800
1062
  - **Works with**: Both built-in behaviors and custom behaviors
801
1063
 
1064
+ #### `@EventHandler(event)`
1065
+
1066
+ Marks a class as an event consumer.
1067
+
1068
+ - **Parameters**: `event` - The event class this consumer handles
1069
+ - **Usage**: Apply to consumer classes that implement `IEventConsumer`
1070
+
1071
+ #### `@Critical(options?)`
1072
+
1073
+ Marks an event consumer as critical. Critical consumers run sequentially in order.
1074
+
1075
+ - **Parameters**:
1076
+ - `options.order` - Execution order among critical consumers (lower numbers first, default: 0)
1077
+ - **Usage**: Apply to consumer classes alongside `@EventHandler`
1078
+ - **Behavior**: If a critical consumer fails, remaining critical consumers are skipped and non-critical consumers don't run
1079
+
1080
+ #### `@NonCritical()`
1081
+
1082
+ Marks an event consumer as non-critical. Non-critical consumers run in parallel after critical consumers complete.
1083
+
1084
+ - **Parameters**: None
1085
+ - **Usage**: Apply to consumer classes alongside `@EventHandler`
1086
+ - **Behavior**: Fire-and-forget - failures are logged but don't affect the publish result
1087
+ - **Note**: Consumers without `@Critical` or `@NonCritical` default to non-critical
1088
+
802
1089
  ### Services
803
1090
 
804
1091
  #### `MediatorBus`
805
1092
 
806
- The main service for sending commands and queries.
1093
+ The main service for sending commands, queries, and events.
807
1094
 
808
1095
  ##### Methods
809
1096
 
@@ -813,7 +1100,7 @@ Sends a command to its registered handler.
813
1100
 
814
1101
  - **Parameters**: `command` - The command instance to execute
815
1102
  - **Returns**: Promise that resolves when the command is executed
816
- - **Throws**: Error if no handler is registered for the command
1103
+ - **Throws**: `HandlerNotFoundException` if no handler is registered for the command
817
1104
 
818
1105
  **`query<TQuery, TResult>(query: TQuery): Promise<TResult>`**
819
1106
 
@@ -821,7 +1108,18 @@ Executes a query through its registered handler.
821
1108
 
822
1109
  - **Parameters**: `query` - The query instance to execute
823
1110
  - **Returns**: Promise that resolves with the query result
824
- - **Throws**: Error if no handler is registered for the query
1111
+ - **Throws**: `HandlerNotFoundException` if no handler is registered for the query
1112
+
1113
+ **`publish<TEvent>(event: TEvent): Promise<EventPublishResult>`**
1114
+
1115
+ Publishes an event to all registered consumers.
1116
+
1117
+ - **Parameters**: `event` - The event instance to publish
1118
+ - **Returns**: Promise with `EventPublishResult` containing:
1119
+ - `totalHandlers` - Total number of consumers
1120
+ - `criticalSucceeded` - Number of critical consumers that completed
1121
+ - `nonCriticalDispatched` - Number of non-critical consumers dispatched
1122
+ - **Throws**: Error if any critical consumer fails
825
1123
 
826
1124
  ### Module Configuration
827
1125
 
@@ -0,0 +1,61 @@
1
+ export { EventCriticality, EventCriticalityMetadata } from '../interfaces/event-criticality.interface.js';
2
+ export declare const EVENT_CRITICALITY_METADATA = "EVENT_CRITICALITY_METADATA";
3
+ /**
4
+ * Options for critical event handlers
5
+ */
6
+ export interface CriticalOptions {
7
+ /**
8
+ * Execution order within critical handlers.
9
+ * Lower numbers execute first.
10
+ * Default: 0
11
+ */
12
+ order?: number;
13
+ }
14
+ /**
15
+ * Decorator to mark an event handler as critical.
16
+ * Critical handlers:
17
+ * - Run sequentially in the order specified
18
+ * - Must complete before non-critical handlers start
19
+ * - If one fails, the publish operation fails (remaining critical handlers are skipped)
20
+ * - Are awaited by the caller
21
+ *
22
+ * @param options - Optional configuration (order)
23
+ * @returns Class decorator
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * @EventHandler(OrderPlacedEvent)
28
+ * @Critical({ order: 1 })
29
+ * export class ReserveInventoryHandler implements IEventConsumer<OrderPlacedEvent> {
30
+ * async handle(event: OrderPlacedEvent): Promise<void> {
31
+ * await this.inventoryService.reserve(event.items);
32
+ * }
33
+ * }
34
+ * ```
35
+ */
36
+ export declare const Critical: (options?: CriticalOptions) => ClassDecorator;
37
+ /**
38
+ * Decorator to explicitly mark an event handler as non-critical.
39
+ * Non-critical handlers:
40
+ * - Run in parallel after all critical handlers complete
41
+ * - Fire and forget (not awaited by the caller)
42
+ * - Failures are logged but don't affect the publish result
43
+ * - Don't block the caller
44
+ *
45
+ * Note: Handlers without @Critical or @NonCritical are non-critical by default.
46
+ *
47
+ * @returns Class decorator
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * @EventHandler(OrderPlacedEvent)
52
+ * @NonCritical()
53
+ * export class SendOrderConfirmationEmail implements IEventConsumer<OrderPlacedEvent> {
54
+ * async handle(event: OrderPlacedEvent): Promise<void> {
55
+ * await this.emailService.send(event.customerEmail, 'order_confirmed');
56
+ * }
57
+ * }
58
+ * ```
59
+ */
60
+ export declare const NonCritical: () => ClassDecorator;
61
+ //# sourceMappingURL=event-criticality.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-criticality.decorator.d.ts","sourceRoot":"","sources":["../../../src/lib/decorators/event-criticality.decorator.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAC;AAE1G,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,QAAQ,GAAI,UAAS,eAAoB,KAAG,cAMxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,QAAO,cAM9B,CAAC"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NonCritical = exports.Critical = exports.EVENT_CRITICALITY_METADATA = exports.EventCriticality = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const event_criticality_interface_js_1 = require("../interfaces/event-criticality.interface.js");
6
+ // Re-export types for backward compatibility
7
+ var event_criticality_interface_js_2 = require("../interfaces/event-criticality.interface.js");
8
+ Object.defineProperty(exports, "EventCriticality", { enumerable: true, get: function () { return event_criticality_interface_js_2.EventCriticality; } });
9
+ exports.EVENT_CRITICALITY_METADATA = 'EVENT_CRITICALITY_METADATA';
10
+ /**
11
+ * Decorator to mark an event handler as critical.
12
+ * Critical handlers:
13
+ * - Run sequentially in the order specified
14
+ * - Must complete before non-critical handlers start
15
+ * - If one fails, the publish operation fails (remaining critical handlers are skipped)
16
+ * - Are awaited by the caller
17
+ *
18
+ * @param options - Optional configuration (order)
19
+ * @returns Class decorator
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * @EventHandler(OrderPlacedEvent)
24
+ * @Critical({ order: 1 })
25
+ * export class ReserveInventoryHandler implements IEventConsumer<OrderPlacedEvent> {
26
+ * async handle(event: OrderPlacedEvent): Promise<void> {
27
+ * await this.inventoryService.reserve(event.items);
28
+ * }
29
+ * }
30
+ * ```
31
+ */
32
+ const Critical = (options = {}) => {
33
+ const metadata = {
34
+ criticality: event_criticality_interface_js_1.EventCriticality.CRITICAL,
35
+ order: options.order ?? 0,
36
+ };
37
+ return (0, common_1.SetMetadata)(exports.EVENT_CRITICALITY_METADATA, metadata);
38
+ };
39
+ exports.Critical = Critical;
40
+ /**
41
+ * Decorator to explicitly mark an event handler as non-critical.
42
+ * Non-critical handlers:
43
+ * - Run in parallel after all critical handlers complete
44
+ * - Fire and forget (not awaited by the caller)
45
+ * - Failures are logged but don't affect the publish result
46
+ * - Don't block the caller
47
+ *
48
+ * Note: Handlers without @Critical or @NonCritical are non-critical by default.
49
+ *
50
+ * @returns Class decorator
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * @EventHandler(OrderPlacedEvent)
55
+ * @NonCritical()
56
+ * export class SendOrderConfirmationEmail implements IEventConsumer<OrderPlacedEvent> {
57
+ * async handle(event: OrderPlacedEvent): Promise<void> {
58
+ * await this.emailService.send(event.customerEmail, 'order_confirmed');
59
+ * }
60
+ * }
61
+ * ```
62
+ */
63
+ const NonCritical = () => {
64
+ const metadata = {
65
+ criticality: event_criticality_interface_js_1.EventCriticality.NON_CRITICAL,
66
+ order: 0,
67
+ };
68
+ return (0, common_1.SetMetadata)(exports.EVENT_CRITICALITY_METADATA, metadata);
69
+ };
70
+ exports.NonCritical = NonCritical;
71
+ //# sourceMappingURL=event-criticality.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-criticality.decorator.js","sourceRoot":"","sources":["../../../src/lib/decorators/event-criticality.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAC7C,iGAGsD;AAEtD,6CAA6C;AAC7C,+FAA0G;AAAjG,kIAAA,gBAAgB,OAAA;AAEZ,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAcvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,MAAM,QAAQ,GAAG,CAAC,UAA2B,EAAE,EAAkB,EAAE;IACxE,MAAM,QAAQ,GAA6B;QACzC,WAAW,EAAE,iDAAgB,CAAC,QAAQ;QACtC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAC1B,CAAC;IACF,OAAO,IAAA,oBAAW,EAAC,kCAA0B,EAAE,QAAQ,CAAC,CAAC;AAC3D,CAAC,CAAC;AANW,QAAA,QAAQ,YAMnB;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACI,MAAM,WAAW,GAAG,GAAmB,EAAE;IAC9C,MAAM,QAAQ,GAA6B;QACzC,WAAW,EAAE,iDAAgB,CAAC,YAAY;QAC1C,KAAK,EAAE,CAAC;KACT,CAAC;IACF,OAAO,IAAA,oBAAW,EAAC,kCAA0B,EAAE,QAAQ,CAAC,CAAC;AAC3D,CAAC,CAAC;AANW,QAAA,WAAW,eAMtB"}
@@ -0,0 +1,21 @@
1
+ import { IEvent } from '../interfaces/index.js';
2
+ export declare const EVENT_HANDLER_METADATA = "EVENT_HANDLER_METADATA";
3
+ /**
4
+ * Decorator to mark a class as an event consumer.
5
+ * Multiple consumers can subscribe to the same event.
6
+ *
7
+ * @param event - The event class that this consumer handles
8
+ * @returns Class decorator
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * @EventHandler(UserCreatedEvent)
13
+ * export class SendWelcomeEmailConsumer implements IEventConsumer<UserCreatedEvent> {
14
+ * async handle(event: UserCreatedEvent): Promise<void> {
15
+ * await this.emailService.sendWelcome(event.userEmail);
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export declare const EventHandler: (event: new (...args: any[]) => IEvent) => ClassDecorator;
21
+ //# sourceMappingURL=event-handler.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler.decorator.d.ts","sourceRoot":"","sources":["../../../src/lib/decorators/event-handler.decorator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,eAAO,MAAM,sBAAsB,2BAA2B,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GACvB,OAAO,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,KACpC,cAEF,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventHandler = exports.EVENT_HANDLER_METADATA = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.EVENT_HANDLER_METADATA = 'EVENT_HANDLER_METADATA';
6
+ /**
7
+ * Decorator to mark a class as an event consumer.
8
+ * Multiple consumers can subscribe to the same event.
9
+ *
10
+ * @param event - The event class that this consumer handles
11
+ * @returns Class decorator
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * @EventHandler(UserCreatedEvent)
16
+ * export class SendWelcomeEmailConsumer implements IEventConsumer<UserCreatedEvent> {
17
+ * async handle(event: UserCreatedEvent): Promise<void> {
18
+ * await this.emailService.sendWelcome(event.userEmail);
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ const EventHandler = (event) => {
24
+ return (0, common_1.SetMetadata)(exports.EVENT_HANDLER_METADATA, event);
25
+ };
26
+ exports.EventHandler = EventHandler;
27
+ //# sourceMappingURL=event-handler.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler.decorator.js","sourceRoot":"","sources":["../../../src/lib/decorators/event-handler.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAGhC,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACI,MAAM,YAAY,GAAG,CAC1B,KAAqC,EACrB,EAAE;IAClB,OAAO,IAAA,oBAAW,EAAC,8BAAsB,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC,CAAC;AAJW,QAAA,YAAY,gBAIvB"}
@@ -2,4 +2,6 @@ export * from './command-handler.decorator.js';
2
2
  export * from './query-handler.decorator.js';
3
3
  export * from './pipeline-behavior.decorator.js';
4
4
  export * from './skip-behavior.decorator.js';
5
+ export * from './event-handler.decorator.js';
6
+ export * from './event-criticality.decorator.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC"}
@@ -18,4 +18,6 @@ __exportStar(require("./command-handler.decorator.js"), exports);
18
18
  __exportStar(require("./query-handler.decorator.js"), exports);
19
19
  __exportStar(require("./pipeline-behavior.decorator.js"), exports);
20
20
  __exportStar(require("./skip-behavior.decorator.js"), exports);
21
+ __exportStar(require("./event-handler.decorator.js"), exports);
22
+ __exportStar(require("./event-criticality.decorator.js"), exports);
21
23
  //# sourceMappingURL=index.js.map