@shirudo/ddd-kit 0.15.0 → 1.0.0-rc.1
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/README.md +106 -84
- package/dist/index.d.ts +342 -313
- package/dist/index.js +881 -1
- package/dist/index.js.map +1 -1
- package/dist/result.js +297 -1
- package/dist/result.js.map +1 -1
- package/dist/utils-array.js +241 -1
- package/dist/utils-array.js.map +1 -1
- package/dist/utils.js +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,138 @@ interface IdGenerator {
|
|
|
8
8
|
next: <T extends string>() => Id<T>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Metadata associated with a domain event for traceability and correlation.
|
|
13
|
+
* Used in event-driven architectures to track event flow across services.
|
|
14
|
+
*/
|
|
15
|
+
interface EventMetadata {
|
|
16
|
+
/**
|
|
17
|
+
* Correlation ID for tracing events across multiple services/components.
|
|
18
|
+
* Typically used to group related events in a distributed system.
|
|
19
|
+
*/
|
|
20
|
+
correlationId?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Causation ID referencing the event or command that caused this event.
|
|
23
|
+
* Used to build event chains and understand causality.
|
|
24
|
+
*/
|
|
25
|
+
causationId?: string;
|
|
26
|
+
/**
|
|
27
|
+
* User ID of the person or system that triggered the event.
|
|
28
|
+
*/
|
|
29
|
+
userId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Source service or component that produced the event.
|
|
32
|
+
*/
|
|
33
|
+
source?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Additional custom metadata fields.
|
|
36
|
+
* Allows extensibility for domain-specific metadata.
|
|
37
|
+
*/
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Domain Event represents something meaningful that happened in the domain.
|
|
42
|
+
* Events are immutable and carry information about what occurred.
|
|
43
|
+
*
|
|
44
|
+
* @template T - The event type name (e.g., "OrderCreated")
|
|
45
|
+
* @template P - The event payload type
|
|
46
|
+
*/
|
|
47
|
+
interface DomainEvent<T extends string, P = void> {
|
|
48
|
+
/**
|
|
49
|
+
* The type of the event, used for routing and handling.
|
|
50
|
+
*/
|
|
51
|
+
type: T;
|
|
52
|
+
/**
|
|
53
|
+
* The event payload containing the domain data.
|
|
54
|
+
* Omitted when P is void (events without payload).
|
|
55
|
+
*/
|
|
56
|
+
payload: P;
|
|
57
|
+
/**
|
|
58
|
+
* Timestamp when the event occurred.
|
|
59
|
+
*/
|
|
60
|
+
occurredAt: Date;
|
|
61
|
+
/**
|
|
62
|
+
* Event schema version for handling schema evolution.
|
|
63
|
+
* Required for safe schema migration in event-sourced systems.
|
|
64
|
+
* Use 1 for the initial schema version.
|
|
65
|
+
*/
|
|
66
|
+
version: number;
|
|
67
|
+
/**
|
|
68
|
+
* Optional metadata for traceability, correlation, and auditing.
|
|
69
|
+
* Includes correlationId, causationId, userId, source, and custom fields.
|
|
70
|
+
*/
|
|
71
|
+
metadata?: EventMetadata;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a domain event with default values.
|
|
75
|
+
* Sets occurredAt to current date and version to 1 if not provided.
|
|
76
|
+
*
|
|
77
|
+
* @param type - The event type
|
|
78
|
+
* @param payload - The event payload
|
|
79
|
+
* @param options - Optional event configuration
|
|
80
|
+
* @returns A domain event
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* const event = createDomainEvent("OrderCreated", { orderId: "123" });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
declare function createDomainEvent<T extends string>(type: T, payload?: undefined, options?: {
|
|
88
|
+
occurredAt?: Date;
|
|
89
|
+
version?: number;
|
|
90
|
+
metadata?: EventMetadata;
|
|
91
|
+
}): DomainEvent<T, void>;
|
|
92
|
+
declare function createDomainEvent<T extends string, P>(type: T, payload: P, options?: {
|
|
93
|
+
occurredAt?: Date;
|
|
94
|
+
version?: number;
|
|
95
|
+
metadata?: EventMetadata;
|
|
96
|
+
}): DomainEvent<T, P>;
|
|
97
|
+
/**
|
|
98
|
+
* Creates a domain event with metadata for traceability.
|
|
99
|
+
* Convenience function for creating events with correlation and causation IDs.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const event = createDomainEventWithMetadata(
|
|
104
|
+
* "OrderCreated",
|
|
105
|
+
* { orderId: "123" },
|
|
106
|
+
* { correlationId: "corr-123", causationId: "cmd-456", userId: "user-789" }
|
|
107
|
+
* );
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function createDomainEventWithMetadata<T extends string, P>(type: T, payload: P, metadata: EventMetadata, options?: {
|
|
111
|
+
occurredAt?: Date;
|
|
112
|
+
version?: number;
|
|
113
|
+
}): DomainEvent<T, P>;
|
|
114
|
+
/**
|
|
115
|
+
* Copies metadata from a source event to a new event.
|
|
116
|
+
* Useful for maintaining correlation chains in event-driven architectures.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const newEvent = createDomainEvent(
|
|
121
|
+
* "OrderShipped",
|
|
122
|
+
* { orderId: "123" },
|
|
123
|
+
* { metadata: copyMetadata(previousEvent, { causationId: previousEvent.type }) }
|
|
124
|
+
* );
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function copyMetadata(sourceEvent: DomainEvent<string, unknown>, additionalMetadata?: Partial<EventMetadata>): EventMetadata;
|
|
128
|
+
/**
|
|
129
|
+
* Merges multiple metadata objects into one.
|
|
130
|
+
* Later metadata objects override earlier ones for the same keys.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const metadata = mergeMetadata(
|
|
135
|
+
* { correlationId: "corr-123" },
|
|
136
|
+
* { userId: "user-456" },
|
|
137
|
+
* { source: "order-service" }
|
|
138
|
+
* );
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
declare function mergeMetadata(...metadataObjects: Array<EventMetadata | undefined>): EventMetadata;
|
|
142
|
+
|
|
11
143
|
/**
|
|
12
144
|
* Functional definition of an Entity via its capability.
|
|
13
145
|
* An object is identifiable if it has an id.
|
|
@@ -292,36 +424,28 @@ interface AggregateConfig {
|
|
|
292
424
|
autoVersionBump?: boolean;
|
|
293
425
|
}
|
|
294
426
|
/**
|
|
295
|
-
* Base class for
|
|
427
|
+
* Base class for Aggregate Roots without Event Sourcing.
|
|
296
428
|
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
429
|
+
* In DDD (Evans), an Aggregate is a cluster of objects — root entity, child entities,
|
|
430
|
+
* and value objects — treated as a unit for consistency. The **Aggregate Root** is the
|
|
431
|
+
* root entity that represents the aggregate externally and is the only entry point
|
|
432
|
+
* for external code. This class serves as both: it IS the root entity and it contains
|
|
433
|
+
* the aggregate state (`TState`) which holds child entities and value objects.
|
|
300
434
|
*
|
|
301
|
-
*
|
|
302
|
-
* - Identity (id)
|
|
303
|
-
* -
|
|
304
|
-
* -
|
|
305
|
-
*
|
|
306
|
-
* Adds Aggregate Root specific functionality:
|
|
307
|
-
* - Version management (for Optimistic Concurrency Control)
|
|
308
|
-
* - Domain events tracking
|
|
435
|
+
* Provides:
|
|
436
|
+
* - Identity (id) and state management (via `Entity`)
|
|
437
|
+
* - Version management for optimistic concurrency control
|
|
438
|
+
* - Domain event tracking for side-effects
|
|
309
439
|
* - Snapshot support for performance optimization
|
|
310
440
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
* - Value objects (immutable objects)
|
|
441
|
+
* All changes to child entities within `TState` are versioned through this root.
|
|
442
|
+
* Use `setState()` for state mutations to ensure invariant validation.
|
|
314
443
|
*
|
|
315
|
-
*
|
|
316
|
-
* applies to the entire aggregate, including all child entities.
|
|
317
|
-
*
|
|
318
|
-
* Implements `IAggregateRoot<TId>` to mark this as an Aggregate Root Entity.
|
|
319
|
-
*
|
|
320
|
-
* Use this class when you don't need Event Sourcing but still want
|
|
321
|
-
* aggregate patterns with versioning and state management.
|
|
444
|
+
* For event sourcing, use `EventSourcedAggregate` instead.
|
|
322
445
|
*
|
|
323
446
|
* @template TState - The type of the aggregate state (contains child entities and value objects)
|
|
324
447
|
* @template TId - The type of the aggregate root identifier
|
|
448
|
+
* @template TEvent - The type of domain events recorded by this aggregate (defaults to unknown)
|
|
325
449
|
*
|
|
326
450
|
* @example
|
|
327
451
|
* ```typescript
|
|
@@ -332,14 +456,15 @@ interface AggregateConfig {
|
|
|
332
456
|
* }
|
|
333
457
|
*
|
|
334
458
|
* confirm(): void {
|
|
335
|
-
* this.
|
|
336
|
-
* this.bumpVersion(); // Versions the entire aggregate
|
|
459
|
+
* this.setState({ ...this.state, status: "confirmed" }, true);
|
|
337
460
|
* }
|
|
338
461
|
* }
|
|
339
462
|
* ```
|
|
340
463
|
*/
|
|
341
|
-
declare abstract class AggregateRoot<TState, TId extends Id<string
|
|
342
|
-
|
|
464
|
+
declare abstract class AggregateRoot<TState, TId extends Id<string>, TEvent = unknown> extends Entity<TState, TId> implements IAggregateRoot<TId> {
|
|
465
|
+
private _version;
|
|
466
|
+
get version(): Version;
|
|
467
|
+
protected setVersion(version: Version): void;
|
|
343
468
|
private readonly _config;
|
|
344
469
|
private readonly _autoVersionBump;
|
|
345
470
|
private _domainEvents;
|
|
@@ -347,7 +472,7 @@ declare abstract class AggregateRoot<TState, TId extends Id<string>> extends Ent
|
|
|
347
472
|
* Returns a read-only list of domain events recorded by this aggregate.
|
|
348
473
|
* These events are side-effects of state changes.
|
|
349
474
|
*/
|
|
350
|
-
get domainEvents(): ReadonlyArray<
|
|
475
|
+
get domainEvents(): ReadonlyArray<TEvent>;
|
|
351
476
|
/**
|
|
352
477
|
* Clears the list of recorded domain events.
|
|
353
478
|
* Call this after dispatching the events.
|
|
@@ -360,7 +485,7 @@ declare abstract class AggregateRoot<TState, TId extends Id<string>> extends Ent
|
|
|
360
485
|
*
|
|
361
486
|
* @param event - The domain event to add
|
|
362
487
|
*/
|
|
363
|
-
protected addDomainEvent(event:
|
|
488
|
+
protected addDomainEvent(event: TEvent): void;
|
|
364
489
|
/**
|
|
365
490
|
* Manually bumps the aggregate version.
|
|
366
491
|
* Call this after state changes for Optimistic Concurrency Control.
|
|
@@ -416,7 +541,7 @@ declare abstract class AggregateRoot<TState, TId extends Id<string>> extends Ent
|
|
|
416
541
|
* @template TId - The type of the aggregate root identifier
|
|
417
542
|
* @template TEvent - The union type of all domain events
|
|
418
543
|
*/
|
|
419
|
-
interface
|
|
544
|
+
interface IEventSourcedAggregate<TId extends Id<string>, TEvent extends DomainEvent<string, unknown>> extends IAggregateRoot<TId> {
|
|
420
545
|
/**
|
|
421
546
|
* Returns a read-only list of new, not-yet-persisted events.
|
|
422
547
|
*/
|
|
@@ -446,9 +571,9 @@ interface IAggregateEventSourced<TId extends Id<string>, TEvent extends DomainEv
|
|
|
446
571
|
}
|
|
447
572
|
type Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;
|
|
448
573
|
/**
|
|
449
|
-
*
|
|
574
|
+
* Configuration options for EventSourcedAggregate behavior.
|
|
450
575
|
*/
|
|
451
|
-
interface
|
|
576
|
+
interface EventSourcedAggregateConfig {
|
|
452
577
|
/**
|
|
453
578
|
* Whether to automatically bump the version when applying new events.
|
|
454
579
|
* Defaults to true. Set to false to manually control versioning.
|
|
@@ -456,26 +581,15 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
456
581
|
autoVersionBump?: boolean;
|
|
457
582
|
}
|
|
458
583
|
/**
|
|
459
|
-
* Base class for Event-Sourced Aggregate Roots (
|
|
584
|
+
* Base class for Event-Sourced Aggregate Roots (Vernon, IDDD Chapter 8).
|
|
460
585
|
*
|
|
461
|
-
*
|
|
462
|
-
* The
|
|
586
|
+
* Like `AggregateRoot`, this is both the root entity and the aggregate boundary.
|
|
587
|
+
* The difference is persistence: state is derived from events, not stored directly.
|
|
588
|
+
* Events are the single source of truth — all state changes go through `apply()` → handler.
|
|
463
589
|
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* All changes to child entities are versioned through the Aggregate Root. The version
|
|
469
|
-
* applies to the entire aggregate, including all child entities.
|
|
470
|
-
*
|
|
471
|
-
* Extends `AggregateRoot` with Event Sourcing capabilities:
|
|
472
|
-
* - Event tracking (pendingEvents)
|
|
473
|
-
* - Event handlers for state transitions
|
|
474
|
-
* - Event validation
|
|
475
|
-
* - History replay
|
|
476
|
-
*
|
|
477
|
-
* Use this class when you want Event Sourcing with full event tracking
|
|
478
|
-
* and replay capabilities.
|
|
590
|
+
* Extends `Entity` directly (not `AggregateRoot`) so that `setState()` and
|
|
591
|
+
* `addDomainEvent()` are not available. This enforces the event sourcing pattern
|
|
592
|
+
* at the type level — there is no way to mutate state without going through an event handler.
|
|
479
593
|
*
|
|
480
594
|
* @template TState - The type of the aggregate state (contains child entities and value objects)
|
|
481
595
|
* @template TEvent - The union type of all domain events
|
|
@@ -483,8 +597,7 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
483
597
|
*
|
|
484
598
|
* @example
|
|
485
599
|
* ```typescript
|
|
486
|
-
*
|
|
487
|
-
* class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
|
|
600
|
+
* class Order extends EventSourcedAggregate<OrderState, OrderEvent, OrderId> {
|
|
488
601
|
* confirm(): void {
|
|
489
602
|
* this.apply(createDomainEvent("OrderConfirmed", {}));
|
|
490
603
|
* }
|
|
@@ -498,58 +611,32 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
498
611
|
* }
|
|
499
612
|
* ```
|
|
500
613
|
*/
|
|
501
|
-
declare abstract class
|
|
502
|
-
private
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
*/
|
|
614
|
+
declare abstract class EventSourcedAggregate<TState, TEvent extends DomainEvent<string, unknown>, TId extends Id<string>> extends Entity<TState, TId> implements IEventSourcedAggregate<TId, TEvent> {
|
|
615
|
+
private _version;
|
|
616
|
+
get version(): Version;
|
|
617
|
+
private setVersion;
|
|
618
|
+
private _pendingEvents;
|
|
619
|
+
private readonly _autoVersionBump;
|
|
508
620
|
get pendingEvents(): ReadonlyArray<TEvent>;
|
|
509
|
-
/**
|
|
510
|
-
* Clears the list of pending events.
|
|
511
|
-
* Typically called after the events have been persisted.
|
|
512
|
-
*/
|
|
513
621
|
clearPendingEvents(): void;
|
|
622
|
+
protected constructor(id: TId, initialState: TState, config?: EventSourcedAggregateConfig);
|
|
514
623
|
/**
|
|
515
624
|
* Validates an event before it is applied.
|
|
516
625
|
* Override this method to add custom validation logic.
|
|
517
626
|
* Return `ok(true)` if the event is valid, `err(message)` otherwise.
|
|
518
|
-
*
|
|
519
|
-
* @param event - The event to validate
|
|
520
|
-
* @returns Result indicating if the event is valid
|
|
521
|
-
*
|
|
522
|
-
* @example
|
|
523
|
-
* ```typescript
|
|
524
|
-
* protected validateEvent(event: OrderEvent): Result<true, string> {
|
|
525
|
-
* if (event.type === "OrderShipped" && this.state.status !== "confirmed") {
|
|
526
|
-
* return err("Order must be confirmed before shipping");
|
|
527
|
-
* }
|
|
528
|
-
* return ok(true);
|
|
529
|
-
* }
|
|
530
|
-
* ```
|
|
531
627
|
*/
|
|
532
628
|
protected validateEvent(_event: TEvent): Result<true, string>;
|
|
533
629
|
/**
|
|
534
|
-
* Applies an event to change the state and adds it
|
|
535
|
-
* to the list of pending events.
|
|
630
|
+
* Applies an event to change the state and adds it to pending events.
|
|
536
631
|
* Returns a Result type instead of throwing an error.
|
|
537
632
|
*
|
|
538
633
|
* @param event - The domain event to apply
|
|
539
|
-
* @param isNew -
|
|
540
|
-
* or if it is being loaded from history
|
|
541
|
-
* @returns Result<void, string> - ok if successful, err with error message if validation fails or handler is missing
|
|
634
|
+
* @param isNew - Whether the event is new (needs persisting) or from history replay
|
|
542
635
|
*/
|
|
543
636
|
protected apply(event: TEvent, isNew?: boolean): Result<void, string>;
|
|
544
637
|
/**
|
|
545
|
-
* Applies an event to change the state and adds it
|
|
546
|
-
* to the list of pending events.
|
|
638
|
+
* Applies an event to change the state and adds it to pending events.
|
|
547
639
|
* Throws an error if validation fails or handler is missing.
|
|
548
|
-
*
|
|
549
|
-
* @param event - The domain event to apply
|
|
550
|
-
* @param isNew - Indicates whether the event is new (and needs to be persisted)
|
|
551
|
-
* or if it is being loaded from history
|
|
552
|
-
* @throws Error if event validation fails or handler is missing
|
|
553
640
|
*/
|
|
554
641
|
protected applyUnsafe(event: TEvent, isNew?: boolean): void;
|
|
555
642
|
/**
|
|
@@ -560,41 +647,17 @@ declare abstract class AggregateEventSourced<TState, TEvent extends DomainEvent<
|
|
|
560
647
|
/**
|
|
561
648
|
* Reconstitutes the aggregate from an event history.
|
|
562
649
|
* Sets the version to the number of events in the history.
|
|
563
|
-
*
|
|
564
|
-
* @param history - An ordered list of past events
|
|
565
650
|
*/
|
|
566
651
|
loadFromHistory(history: TEvent[]): Result<void, string>;
|
|
567
|
-
/**
|
|
568
|
-
* Checks if the aggregate has any pending events.
|
|
569
|
-
*
|
|
570
|
-
* @returns true if there are pending events, false otherwise
|
|
571
|
-
*/
|
|
572
652
|
hasPendingEvents(): boolean;
|
|
573
|
-
/**
|
|
574
|
-
* Returns the number of pending events.
|
|
575
|
-
*
|
|
576
|
-
* @returns The count of pending events
|
|
577
|
-
*/
|
|
578
653
|
getEventCount(): number;
|
|
654
|
+
getLatestEvent(): TEvent | undefined;
|
|
579
655
|
/**
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* @returns The most recent event or undefined if no events exist
|
|
656
|
+
* Creates a snapshot of the current aggregate state.
|
|
583
657
|
*/
|
|
584
|
-
|
|
658
|
+
createSnapshot(): AggregateSnapshot<TState>;
|
|
585
659
|
/**
|
|
586
|
-
* Restores the aggregate from a snapshot and applies events that occurred after
|
|
587
|
-
* This is more efficient than replaying all events from the beginning.
|
|
588
|
-
*
|
|
589
|
-
* @param snapshot - The snapshot to restore from
|
|
590
|
-
* @param eventsAfterSnapshot - Events that occurred after the snapshot was taken
|
|
591
|
-
*
|
|
592
|
-
* @example
|
|
593
|
-
* ```typescript
|
|
594
|
-
* const snapshot = await snapshotRepository.getLatest(aggregateId);
|
|
595
|
-
* const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);
|
|
596
|
-
* aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);
|
|
597
|
-
* ```
|
|
660
|
+
* Restores the aggregate from a snapshot and applies events that occurred after.
|
|
598
661
|
*/
|
|
599
662
|
restoreFromSnapshotWithEvents(snapshot: AggregateSnapshot<TState>, eventsAfterSnapshot: TEvent[]): Result<void, string>;
|
|
600
663
|
/**
|
|
@@ -612,164 +675,32 @@ type Version = number & {
|
|
|
612
675
|
readonly __v: true;
|
|
613
676
|
};
|
|
614
677
|
/**
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*/
|
|
618
|
-
interface EventMetadata {
|
|
619
|
-
/**
|
|
620
|
-
* Correlation ID for tracing events across multiple services/components.
|
|
621
|
-
* Typically used to group related events in a distributed system.
|
|
622
|
-
*/
|
|
623
|
-
correlationId?: string;
|
|
624
|
-
/**
|
|
625
|
-
* Causation ID referencing the event or command that caused this event.
|
|
626
|
-
* Used to build event chains and understand causality.
|
|
627
|
-
*/
|
|
628
|
-
causationId?: string;
|
|
629
|
-
/**
|
|
630
|
-
* User ID of the person or system that triggered the event.
|
|
631
|
-
*/
|
|
632
|
-
userId?: string;
|
|
633
|
-
/**
|
|
634
|
-
* Source service or component that produced the event.
|
|
635
|
-
*/
|
|
636
|
-
source?: string;
|
|
637
|
-
/**
|
|
638
|
-
* Additional custom metadata fields.
|
|
639
|
-
* Allows extensibility for domain-specific metadata.
|
|
640
|
-
*/
|
|
641
|
-
[key: string]: unknown;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Domain Event represents something meaningful that happened in the domain.
|
|
645
|
-
* Events are immutable and carry information about what occurred.
|
|
678
|
+
* Lightweight functional aggregate state — state + version, no event sourcing.
|
|
679
|
+
* This is a data projection, not a full Aggregate (which requires identity via IAggregateRoot).
|
|
646
680
|
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*/
|
|
650
|
-
interface DomainEvent<T extends string, P> {
|
|
651
|
-
/**
|
|
652
|
-
* The type of the event, used for routing and handling.
|
|
653
|
-
*/
|
|
654
|
-
type: T;
|
|
655
|
-
/**
|
|
656
|
-
* The event payload containing the domain data.
|
|
657
|
-
*/
|
|
658
|
-
payload: P;
|
|
659
|
-
/**
|
|
660
|
-
* Timestamp when the event occurred.
|
|
661
|
-
*/
|
|
662
|
-
occurredAt: Date;
|
|
663
|
-
/**
|
|
664
|
-
* Event schema version for handling schema evolution.
|
|
665
|
-
* Defaults to 1 if not specified. Higher versions indicate schema changes.
|
|
666
|
-
*/
|
|
667
|
-
version?: number;
|
|
668
|
-
/**
|
|
669
|
-
* Optional metadata for traceability, correlation, and auditing.
|
|
670
|
-
* Includes correlationId, causationId, userId, source, and custom fields.
|
|
671
|
-
*/
|
|
672
|
-
metadata?: EventMetadata;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* Structural interface representing an aggregate with state and events.
|
|
677
|
-
* Used for type constraints in repositories and other infrastructure code.
|
|
681
|
+
* For event sourcing, use the class-based `EventSourcedAggregate` which enforces
|
|
682
|
+
* that state changes happen through event handlers.
|
|
678
683
|
*
|
|
679
684
|
* @template State - The type of the aggregate state
|
|
680
|
-
* @template Evt - The union type of all domain events
|
|
681
685
|
*/
|
|
682
|
-
interface
|
|
686
|
+
interface AggregateState<State> {
|
|
683
687
|
state: Readonly<State>;
|
|
684
688
|
version: Version;
|
|
685
|
-
pendingEvents: ReadonlyArray<Evt>;
|
|
686
689
|
}
|
|
687
|
-
declare function aggregate<State, Evt extends DomainEvent<string, unknown>>(state: State, version?: Version): Aggregate<State, Evt>;
|
|
688
|
-
declare function withEvent<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>, evt: E): Aggregate<S, E>;
|
|
689
|
-
declare function bump<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>): Aggregate<S, E>;
|
|
690
|
-
/**
|
|
691
|
-
* Creates a domain event with default values.
|
|
692
|
-
* Sets occurredAt to current date and version to 1 if not provided.
|
|
693
|
-
*
|
|
694
|
-
* @param type - The event type
|
|
695
|
-
* @param payload - The event payload
|
|
696
|
-
* @param options - Optional event configuration
|
|
697
|
-
* @returns A domain event
|
|
698
|
-
*
|
|
699
|
-
* @example
|
|
700
|
-
* ```typescript
|
|
701
|
-
* const event = createDomainEvent("OrderCreated", { orderId: "123" });
|
|
702
|
-
* ```
|
|
703
|
-
*/
|
|
704
|
-
declare function createDomainEvent<T extends string, P>(type: T, payload: P, options?: {
|
|
705
|
-
occurredAt?: Date;
|
|
706
|
-
version?: number;
|
|
707
|
-
metadata?: EventMetadata;
|
|
708
|
-
}): DomainEvent<T, P>;
|
|
709
|
-
/**
|
|
710
|
-
* Creates a domain event with metadata for traceability.
|
|
711
|
-
* Convenience function for creating events with correlation and causation IDs.
|
|
712
|
-
*
|
|
713
|
-
* @param type - The event type
|
|
714
|
-
* @param payload - The event payload
|
|
715
|
-
* @param metadata - Event metadata for traceability
|
|
716
|
-
* @param options - Optional event configuration
|
|
717
|
-
* @returns A domain event with metadata
|
|
718
|
-
*
|
|
719
|
-
* @example
|
|
720
|
-
* ```typescript
|
|
721
|
-
* const event = createDomainEventWithMetadata(
|
|
722
|
-
* "OrderCreated",
|
|
723
|
-
* { orderId: "123" },
|
|
724
|
-
* {
|
|
725
|
-
* correlationId: "corr-123",
|
|
726
|
-
* causationId: "cmd-456",
|
|
727
|
-
* userId: "user-789"
|
|
728
|
-
* }
|
|
729
|
-
* );
|
|
730
|
-
* ```
|
|
731
|
-
*/
|
|
732
|
-
declare function createDomainEventWithMetadata<T extends string, P>(type: T, payload: P, metadata: EventMetadata, options?: {
|
|
733
|
-
occurredAt?: Date;
|
|
734
|
-
version?: number;
|
|
735
|
-
}): DomainEvent<T, P>;
|
|
736
690
|
/**
|
|
737
|
-
*
|
|
738
|
-
* Useful for maintaining correlation chains in event-driven architectures.
|
|
739
|
-
*
|
|
740
|
-
* @param sourceEvent - The source event to copy metadata from
|
|
741
|
-
* @param additionalMetadata - Additional metadata to merge in
|
|
742
|
-
* @returns Event metadata with copied and merged values
|
|
691
|
+
* Creates a lightweight functional aggregate state.
|
|
743
692
|
*
|
|
744
693
|
* @example
|
|
745
694
|
* ```typescript
|
|
746
|
-
* const
|
|
747
|
-
* "OrderShipped",
|
|
748
|
-
* { orderId: "123" },
|
|
749
|
-
* {
|
|
750
|
-
* metadata: copyMetadata(previousEvent, { causationId: previousEvent.type })
|
|
751
|
-
* }
|
|
752
|
-
* );
|
|
695
|
+
* const order = aggregate<OrderState>({ status: "draft", items: [] });
|
|
753
696
|
* ```
|
|
754
697
|
*/
|
|
755
|
-
declare function
|
|
698
|
+
declare function aggregate<State>(state: State, version?: Version): AggregateState<State>;
|
|
756
699
|
/**
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
* @param metadataObjects - Array of metadata objects to merge
|
|
761
|
-
* @returns Merged event metadata
|
|
762
|
-
*
|
|
763
|
-
* @example
|
|
764
|
-
* ```typescript
|
|
765
|
-
* const metadata = mergeMetadata(
|
|
766
|
-
* { correlationId: "corr-123" },
|
|
767
|
-
* { userId: "user-456" },
|
|
768
|
-
* { source: "order-service" }
|
|
769
|
-
* );
|
|
770
|
-
* ```
|
|
700
|
+
* Bumps the version of a functional aggregate state.
|
|
701
|
+
* Returns a new aggregate state with incremented version.
|
|
771
702
|
*/
|
|
772
|
-
declare function
|
|
703
|
+
declare function bump<S>(agg: AggregateState<S>): AggregateState<S>;
|
|
773
704
|
/**
|
|
774
705
|
* Snapshot of an aggregate state at a specific point in time.
|
|
775
706
|
* Used for optimizing event replay by starting from a snapshot
|
|
@@ -792,25 +723,24 @@ interface AggregateSnapshot<TState> {
|
|
|
792
723
|
snapshotAt: Date;
|
|
793
724
|
}
|
|
794
725
|
/**
|
|
795
|
-
* Checks if two aggregates are the same (same ID and version).
|
|
726
|
+
* Checks if two aggregates are at the same version (same ID and version).
|
|
796
727
|
* Useful for optimistic concurrency control checks.
|
|
797
728
|
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
* @returns true if both aggregates have the same ID and version
|
|
729
|
+
* Note: Two aggregates with the same ID ARE the same aggregate (identity).
|
|
730
|
+
* This function checks if they are at the same version — i.e., no concurrent modification.
|
|
801
731
|
*
|
|
802
732
|
* @example
|
|
803
733
|
* ```typescript
|
|
804
|
-
* const
|
|
734
|
+
* const before = await repository.getById(id);
|
|
805
735
|
* // ... some operations ...
|
|
806
|
-
* const
|
|
736
|
+
* const after = await repository.getById(id);
|
|
807
737
|
*
|
|
808
|
-
* if (!
|
|
738
|
+
* if (!sameVersion(before, after)) {
|
|
809
739
|
* throw new Error("Aggregate was modified by another process");
|
|
810
740
|
* }
|
|
811
741
|
* ```
|
|
812
742
|
*/
|
|
813
|
-
declare function
|
|
743
|
+
declare function sameVersion<TId extends Id<string>>(a: {
|
|
814
744
|
id: TId;
|
|
815
745
|
version: Version;
|
|
816
746
|
}, b: {
|
|
@@ -906,29 +836,58 @@ interface Command {
|
|
|
906
836
|
*/
|
|
907
837
|
type CommandHandler<C extends Command, R> = (cmd: C) => Promise<Result<R, string>>;
|
|
908
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Type map for command types to their return types.
|
|
841
|
+
* Used to improve type inference in CommandBus.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```typescript
|
|
845
|
+
* type MyCommandMap = {
|
|
846
|
+
* CreateOrder: OrderId;
|
|
847
|
+
* CancelOrder: void;
|
|
848
|
+
* };
|
|
849
|
+
*
|
|
850
|
+
* const bus = new CommandBus<MyCommandMap>();
|
|
851
|
+
* const result = await bus.execute({ type: "CreateOrder", ... });
|
|
852
|
+
* // result: Result<OrderId, string> ← automatically inferred
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
type CommandTypeMap = Record<string, unknown>;
|
|
909
856
|
/**
|
|
910
857
|
* Command Bus interface for dispatching commands to their handlers.
|
|
911
858
|
* Provides a centralized way to execute commands with handler registration.
|
|
912
859
|
*
|
|
860
|
+
* Supports an optional type map (`TMap`) for automatic return type inference.
|
|
861
|
+
* Without a type map, the return type must be specified manually or defaults to `unknown`.
|
|
862
|
+
*
|
|
863
|
+
* @template TMap - Optional mapping from command type strings to return types
|
|
864
|
+
*
|
|
913
865
|
* @example
|
|
914
866
|
* ```typescript
|
|
867
|
+
* // With type map (recommended) – return type is inferred
|
|
868
|
+
* type MyCommands = { CreateOrder: OrderId; CancelOrder: void };
|
|
869
|
+
* const bus = new CommandBus<MyCommands>();
|
|
870
|
+
* const result = await bus.execute({ type: "CreateOrder", ... });
|
|
871
|
+
* // result: Result<OrderId, string>
|
|
872
|
+
*
|
|
873
|
+
* // Without type map – works like before
|
|
915
874
|
* const bus = new CommandBus();
|
|
916
875
|
* bus.register("CreateOrder", createOrderHandler);
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
* type: "CreateOrder",
|
|
920
|
-
* customerId: "123",
|
|
921
|
-
* items: [...]
|
|
922
|
-
* });
|
|
876
|
+
* const result = await bus.execute({ type: "CreateOrder", ... });
|
|
877
|
+
* // result: Result<unknown, string>
|
|
923
878
|
* ```
|
|
924
879
|
*/
|
|
925
|
-
interface ICommandBus {
|
|
880
|
+
interface ICommandBus<TMap extends CommandTypeMap = CommandTypeMap> {
|
|
926
881
|
/**
|
|
927
882
|
* Executes a command by dispatching it to the registered handler.
|
|
883
|
+
* When a type map is provided, the return type is inferred from the command type.
|
|
928
884
|
*
|
|
929
885
|
* @param command - The command to execute
|
|
930
886
|
* @returns Result containing the success value or error message
|
|
931
887
|
*/
|
|
888
|
+
execute<C extends Command & {
|
|
889
|
+
type: keyof TMap & string;
|
|
890
|
+
}>(command: C): Promise<Result<TMap[C["type"]], string>>;
|
|
932
891
|
execute<C extends Command, R>(command: C): Promise<Result<R, string>>;
|
|
933
892
|
/**
|
|
934
893
|
* Registers a handler for a specific command type.
|
|
@@ -942,6 +901,10 @@ interface ICommandBus {
|
|
|
942
901
|
* Simple in-memory command bus implementation.
|
|
943
902
|
* Handlers are stored in a Map and dispatched based on command type.
|
|
944
903
|
*
|
|
904
|
+
* Supports an optional type map (`TMap`) for automatic return type inference.
|
|
905
|
+
* When `TMap` is provided, `execute()` infers the result type from the command type.
|
|
906
|
+
* Without `TMap`, it works like before (return type defaults to `unknown` or can be specified manually).
|
|
907
|
+
*
|
|
945
908
|
* **Note:** This is a basic implementation suitable for development and simple use cases.
|
|
946
909
|
* For production environments, consider implementing or using a more feature-rich bus that includes:
|
|
947
910
|
* - Middleware/Pipeline support (logging, validation, authorization)
|
|
@@ -954,20 +917,28 @@ interface ICommandBus {
|
|
|
954
917
|
* The `CommandHandler` type can still be used with external production-grade buses
|
|
955
918
|
* (e.g., RabbitMQ, AWS SQS) while maintaining type safety.
|
|
956
919
|
*
|
|
920
|
+
* @template TMap - Optional mapping from command type strings to return types
|
|
921
|
+
*
|
|
957
922
|
* @example
|
|
958
923
|
* ```typescript
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
*
|
|
924
|
+
* // With type map – full inference
|
|
925
|
+
* type Commands = { CreateOrder: OrderId; CancelOrder: void };
|
|
926
|
+
* const bus = new CommandBus<Commands>();
|
|
927
|
+
* const result = await bus.execute({ type: "CreateOrder", ... });
|
|
928
|
+
* // result: Result<OrderId, string>
|
|
964
929
|
*
|
|
930
|
+
* // Without type map – same as before
|
|
931
|
+
* const bus = new CommandBus();
|
|
932
|
+
* bus.register("CreateOrder", async (cmd) => ok(orderId));
|
|
965
933
|
* const result = await bus.execute({ type: "CreateOrder", ... });
|
|
966
934
|
* ```
|
|
967
935
|
*/
|
|
968
|
-
declare class CommandBus implements ICommandBus {
|
|
936
|
+
declare class CommandBus<TMap extends CommandTypeMap = CommandTypeMap> implements ICommandBus<TMap> {
|
|
969
937
|
private readonly handlers;
|
|
970
938
|
register<C extends Command, R>(commandType: C["type"], handler: CommandHandler<C, R>): void;
|
|
939
|
+
execute<C extends Command & {
|
|
940
|
+
type: keyof TMap & string;
|
|
941
|
+
}>(command: C): Promise<Result<TMap[C["type"]], string>>;
|
|
971
942
|
execute<C extends Command, R>(command: C): Promise<Result<R, string>>;
|
|
972
943
|
}
|
|
973
944
|
|
|
@@ -1000,7 +971,9 @@ type EventHandler<Evt> = (event: Evt) => Promise<void> | void;
|
|
|
1000
971
|
* await bus.publish([orderCreatedEvent, orderShippedEvent]);
|
|
1001
972
|
* ```
|
|
1002
973
|
*/
|
|
1003
|
-
interface EventBus<Evt
|
|
974
|
+
interface EventBus<Evt extends {
|
|
975
|
+
type: string;
|
|
976
|
+
}> {
|
|
1004
977
|
/**
|
|
1005
978
|
* Publishes events to all subscribed handlers.
|
|
1006
979
|
* All handlers for each event type will be called.
|
|
@@ -1026,14 +999,26 @@ interface EventBus<Evt> {
|
|
|
1026
999
|
* unsubscribe();
|
|
1027
1000
|
* ```
|
|
1028
1001
|
*/
|
|
1029
|
-
subscribe: <T extends Evt>(eventType:
|
|
1002
|
+
subscribe: <T extends Evt>(eventType: Evt["type"], handler: EventHandler<T>) => () => void;
|
|
1003
|
+
/**
|
|
1004
|
+
* Subscribes to the next occurrence of an event type.
|
|
1005
|
+
* Returns a Promise that resolves with the event data.
|
|
1006
|
+
* Automatically unsubscribes after the first event.
|
|
1007
|
+
*
|
|
1008
|
+
* @param eventType - The event type to wait for
|
|
1009
|
+
* @returns A Promise that resolves with the event
|
|
1010
|
+
*
|
|
1011
|
+
* @example
|
|
1012
|
+
* ```typescript
|
|
1013
|
+
* const event = await bus.once("OrderCreated");
|
|
1014
|
+
* console.log("Order created:", event.payload.orderId);
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
once: <T extends Evt>(eventType: Evt["type"]) => Promise<T>;
|
|
1030
1018
|
}
|
|
1031
1019
|
interface Outbox<Evt> {
|
|
1032
1020
|
add: (events: ReadonlyArray<Evt>) => Promise<void>;
|
|
1033
1021
|
}
|
|
1034
|
-
interface Clock {
|
|
1035
|
-
now: () => Date;
|
|
1036
|
-
}
|
|
1037
1022
|
|
|
1038
1023
|
interface UnitOfWork {
|
|
1039
1024
|
transactional<T>(fn: () => Promise<T>): Promise<T>;
|
|
@@ -1063,7 +1048,9 @@ type RepoProvider<R> = (uow: UnitOfWork) => R;
|
|
|
1063
1048
|
* );
|
|
1064
1049
|
* ```
|
|
1065
1050
|
*/
|
|
1066
|
-
declare function withCommit<Evt
|
|
1051
|
+
declare function withCommit<Evt extends {
|
|
1052
|
+
type: string;
|
|
1053
|
+
}, R>(deps: {
|
|
1067
1054
|
outbox: Outbox<Evt>;
|
|
1068
1055
|
bus?: EventBus<Evt>;
|
|
1069
1056
|
uow: UnitOfWork;
|
|
@@ -1147,29 +1134,57 @@ interface Query {
|
|
|
1147
1134
|
*/
|
|
1148
1135
|
type QueryHandler<Q extends Query, R> = (query: Q) => Promise<R>;
|
|
1149
1136
|
|
|
1137
|
+
/**
|
|
1138
|
+
* Type map for query types to their return types.
|
|
1139
|
+
* Used to improve type inference in QueryBus.
|
|
1140
|
+
*
|
|
1141
|
+
* @example
|
|
1142
|
+
* ```typescript
|
|
1143
|
+
* type MyQueryMap = {
|
|
1144
|
+
* GetOrder: Order | null;
|
|
1145
|
+
* ListOrders: Order[];
|
|
1146
|
+
* };
|
|
1147
|
+
*
|
|
1148
|
+
* const bus = new QueryBus<MyQueryMap>();
|
|
1149
|
+
* const result = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
1150
|
+
* // result: Result<Order | null, string> ← automatically inferred
|
|
1151
|
+
* ```
|
|
1152
|
+
*/
|
|
1153
|
+
type QueryTypeMap = Record<string, unknown>;
|
|
1150
1154
|
/**
|
|
1151
1155
|
* Query Bus interface for dispatching queries to their handlers.
|
|
1152
1156
|
* Provides a centralized way to execute queries with handler registration.
|
|
1153
1157
|
*
|
|
1158
|
+
* Supports an optional type map (`TMap`) for automatic return type inference.
|
|
1159
|
+
* Without a type map, the return type must be specified manually or defaults to `unknown`.
|
|
1160
|
+
*
|
|
1161
|
+
* @template TMap - Optional mapping from query type strings to return types
|
|
1162
|
+
*
|
|
1154
1163
|
* @example
|
|
1155
1164
|
* ```typescript
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1165
|
+
* // With type map (recommended) – return type is inferred
|
|
1166
|
+
* type MyQueries = { GetOrder: Order | null; ListOrders: Order[] };
|
|
1167
|
+
* const bus = new QueryBus<MyQueries>();
|
|
1168
|
+
* const result = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
1169
|
+
* // result: Result<Order | null, string>
|
|
1158
1170
|
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1171
|
+
* // Without type map – works like before
|
|
1172
|
+
* const bus = new QueryBus();
|
|
1173
|
+
* const result = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
1174
|
+
* // result: Result<unknown, string>
|
|
1163
1175
|
* ```
|
|
1164
1176
|
*/
|
|
1165
|
-
interface IQueryBus {
|
|
1177
|
+
interface IQueryBus<TMap extends QueryTypeMap = QueryTypeMap> {
|
|
1166
1178
|
/**
|
|
1167
1179
|
* Executes a query by dispatching it to the registered handler.
|
|
1168
|
-
*
|
|
1180
|
+
* When a type map is provided, the return type is inferred from the query type.
|
|
1169
1181
|
*
|
|
1170
1182
|
* @param query - The query to execute
|
|
1171
|
-
* @returns Result containing the query result if successful, or an error message
|
|
1183
|
+
* @returns Result containing the query result if successful, or an error message
|
|
1172
1184
|
*/
|
|
1185
|
+
execute<Q extends Query & {
|
|
1186
|
+
type: keyof TMap & string;
|
|
1187
|
+
}>(query: Q): Promise<Result<TMap[Q["type"]], string>>;
|
|
1173
1188
|
execute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;
|
|
1174
1189
|
/**
|
|
1175
1190
|
* Executes a query by dispatching it to the registered handler.
|
|
@@ -1179,6 +1194,9 @@ interface IQueryBus {
|
|
|
1179
1194
|
* @returns The query result
|
|
1180
1195
|
* @throws Error if no handler is registered for the query type
|
|
1181
1196
|
*/
|
|
1197
|
+
executeUnsafe<Q extends Query & {
|
|
1198
|
+
type: keyof TMap & string;
|
|
1199
|
+
}>(query: Q): Promise<TMap[Q["type"]]>;
|
|
1182
1200
|
executeUnsafe<Q extends Query, R>(query: Q): Promise<R>;
|
|
1183
1201
|
/**
|
|
1184
1202
|
* Registers a handler for a specific query type.
|
|
@@ -1188,15 +1206,14 @@ interface IQueryBus {
|
|
|
1188
1206
|
*/
|
|
1189
1207
|
register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
|
|
1190
1208
|
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Type map for query types to their return types.
|
|
1193
|
-
* Used to improve type inference in QueryBus.
|
|
1194
|
-
*/
|
|
1195
|
-
type QueryTypeMap = Record<string, unknown>;
|
|
1196
1209
|
/**
|
|
1197
1210
|
* Simple in-memory query bus implementation.
|
|
1198
1211
|
* Handlers are stored in a Map and dispatched based on query type.
|
|
1199
1212
|
*
|
|
1213
|
+
* Supports an optional type map (`TMap`) for automatic return type inference.
|
|
1214
|
+
* When `TMap` is provided, `execute()` and `executeUnsafe()` infer the result type from the query type.
|
|
1215
|
+
* Without `TMap`, it works like before (return type defaults to `unknown` or can be specified manually).
|
|
1216
|
+
*
|
|
1200
1217
|
* **Note:** This is a basic implementation suitable for development and simple use cases.
|
|
1201
1218
|
* For production environments, consider implementing or using a more feature-rich bus that includes:
|
|
1202
1219
|
* - Middleware/Pipeline support (logging, caching, rate limiting)
|
|
@@ -1209,23 +1226,32 @@ type QueryTypeMap = Record<string, unknown>;
|
|
|
1209
1226
|
* The `QueryHandler` type can still be used with external production-grade buses
|
|
1210
1227
|
* (e.g., RabbitMQ, AWS SQS) while maintaining type safety.
|
|
1211
1228
|
*
|
|
1229
|
+
* @template TMap - Optional mapping from query type strings to return types
|
|
1230
|
+
*
|
|
1212
1231
|
* @example
|
|
1213
1232
|
* ```typescript
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
* });
|
|
1233
|
+
* // With type map – full inference
|
|
1234
|
+
* type Queries = { GetOrder: Order | null; ListOrders: Order[] };
|
|
1235
|
+
* const bus = new QueryBus<Queries>();
|
|
1236
|
+
* const result = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
1237
|
+
* // result: Result<Order | null, string>
|
|
1218
1238
|
*
|
|
1219
|
-
*
|
|
1239
|
+
* // Without type map – same as before
|
|
1240
|
+
* const bus = new QueryBus();
|
|
1241
|
+
* bus.register("GetOrder", async (query) => repository.getById(query.orderId));
|
|
1242
|
+
* const result = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
1220
1243
|
* ```
|
|
1221
1244
|
*/
|
|
1222
|
-
declare class QueryBus<TMap extends QueryTypeMap = QueryTypeMap> implements IQueryBus {
|
|
1245
|
+
declare class QueryBus<TMap extends QueryTypeMap = QueryTypeMap> implements IQueryBus<TMap> {
|
|
1223
1246
|
private readonly handlers;
|
|
1224
1247
|
register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
|
|
1225
1248
|
execute<Q extends Query & {
|
|
1226
|
-
type: keyof TMap;
|
|
1249
|
+
type: keyof TMap & string;
|
|
1227
1250
|
}>(query: Q): Promise<Result<TMap[Q["type"]], string>>;
|
|
1228
1251
|
execute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;
|
|
1252
|
+
executeUnsafe<Q extends Query & {
|
|
1253
|
+
type: keyof TMap & string;
|
|
1254
|
+
}>(query: Q): Promise<TMap[Q["type"]]>;
|
|
1229
1255
|
executeUnsafe<Q extends Query, R>(query: Q): Promise<R>;
|
|
1230
1256
|
}
|
|
1231
1257
|
|
|
@@ -1271,16 +1297,21 @@ declare function guard(cond: boolean, error: string): Result<true, string>;
|
|
|
1271
1297
|
*/
|
|
1272
1298
|
declare class EventBusImpl<Evt extends DomainEvent<string, unknown>> implements EventBus<Evt> {
|
|
1273
1299
|
private readonly handlers;
|
|
1274
|
-
subscribe<T extends Evt>(eventType:
|
|
1300
|
+
subscribe<T extends Evt>(eventType: Evt["type"], handler: EventHandler<T>): () => void;
|
|
1301
|
+
once<T extends Evt>(eventType: Evt["type"]): Promise<T>;
|
|
1275
1302
|
publish(events: ReadonlyArray<Evt>): Promise<void>;
|
|
1276
1303
|
}
|
|
1277
1304
|
|
|
1305
|
+
declare const __specBrand: unique symbol;
|
|
1278
1306
|
/**
|
|
1279
1307
|
* A Specification is a named, standalone object that represents a business rule for a query.
|
|
1280
1308
|
* It is "translatable" into a concrete database query.
|
|
1309
|
+
*
|
|
1310
|
+
* Uses a branded type to carry the generic parameter without requiring
|
|
1311
|
+
* implementors to add a runtime field.
|
|
1281
1312
|
*/
|
|
1282
1313
|
interface ISpecification<T> {
|
|
1283
|
-
readonly
|
|
1314
|
+
readonly [__specBrand]?: T;
|
|
1284
1315
|
}
|
|
1285
1316
|
|
|
1286
1317
|
/**
|
|
@@ -1297,12 +1328,10 @@ interface ISpecification<T> {
|
|
|
1297
1328
|
* Child entities cannot be loaded or saved independently - they exist only
|
|
1298
1329
|
* within the aggregate boundary and are managed through the Aggregate Root.
|
|
1299
1330
|
*
|
|
1300
|
-
* @template
|
|
1301
|
-
* @template TEvent - The union type of all domain events
|
|
1302
|
-
* @template TAgg - The aggregate root type (must be an Aggregate Root Entity)
|
|
1331
|
+
* @template TAgg - The aggregate root type (must implement IAggregateRoot)
|
|
1303
1332
|
* @template TId - The type of the aggregate root identifier
|
|
1304
1333
|
*/
|
|
1305
|
-
interface IRepository<
|
|
1334
|
+
interface IRepository<TAgg extends IAggregateRoot<TId>, TId extends Id<string>> {
|
|
1306
1335
|
getById(id: TId): Promise<TAgg | null>;
|
|
1307
1336
|
findOne(spec: ISpecification<TAgg>): Promise<TAgg | null>;
|
|
1308
1337
|
find(spec: ISpecification<TAgg>): Promise<TAgg[]>;
|
|
@@ -1541,4 +1570,4 @@ declare abstract class ValueObject<T extends object> implements IValueObject<T>
|
|
|
1541
1570
|
toJSON(): Readonly<T>;
|
|
1542
1571
|
}
|
|
1543
1572
|
|
|
1544
|
-
export { type
|
|
1573
|
+
export { type AggregateConfig, AggregateRoot, type AggregateSnapshot, type AggregateState, type Command, CommandBus, type CommandHandler, type DomainEvent, Entity, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, EventSourcedAggregate, type EventSourcedAggregateConfig, type IAggregateRoot, type ICommandBus, type IEntity, type IEventSourcedAggregate, type IQueryBus, type IRepository, type ISpecification, type IValueObject, type Id, type IdGenerator, type Identifiable, type Outbox, type Query, QueryBus, type QueryHandler, type RepoProvider, type UnitOfWork, type VO, ValueObject, type Version, aggregate, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepFreeze, entityIds, findEntityById, guard, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, sameEntity, sameVersion, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, voWithValidationUnsafe, withCommit };
|