@shirudo/ddd-kit 0.9.1 → 0.9.3
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 +157 -94
- package/dist/index.d.ts +441 -31
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -70,30 +70,40 @@ interface DomainEvent<T extends string, P> {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Marker interface for Aggregate Roots.
|
|
73
|
-
* Aggregate Roots are the entry points for modifying aggregates in DDD.
|
|
74
|
-
* They have identity (id) and version for optimistic concurrency control.
|
|
75
73
|
*
|
|
76
|
-
* In Domain-Driven Design, an Aggregate Root is the
|
|
77
|
-
*
|
|
78
|
-
*
|
|
74
|
+
* In Domain-Driven Design, an Aggregate Root is an Entity (the parent Entity of the aggregate).
|
|
75
|
+
* It represents the aggregate externally and is the only object that external code
|
|
76
|
+
* is allowed to hold references to. All access to child entities within the aggregate
|
|
77
|
+
* must go through the Aggregate Root.
|
|
79
78
|
*
|
|
80
|
-
*
|
|
79
|
+
* An Aggregate consists of:
|
|
80
|
+
* - One Aggregate Root (Entity with id + version)
|
|
81
|
+
* - Optional child entities (Entities with id, but no own version)
|
|
82
|
+
* - Optional value objects
|
|
83
|
+
*
|
|
84
|
+
* The Aggregate Root has identity (id) and version for optimistic concurrency control.
|
|
85
|
+
* Child entities exist only within the aggregate boundary and are versioned through
|
|
86
|
+
* the Aggregate Root.
|
|
87
|
+
*
|
|
88
|
+
* @template TId - The type of the aggregate root identifier
|
|
81
89
|
*
|
|
82
90
|
* @example
|
|
83
91
|
* ```typescript
|
|
84
92
|
* class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
|
|
85
|
-
* // Order is an Aggregate Root
|
|
93
|
+
* // Order is an Aggregate Root (an Entity)
|
|
94
|
+
* // OrderState contains child entities (e.g., OrderItem) and value objects
|
|
86
95
|
* }
|
|
87
96
|
* ```
|
|
88
97
|
*/
|
|
89
98
|
interface AggregateRoot<TId extends Id<string>> {
|
|
90
99
|
/**
|
|
91
|
-
* Unique identifier of the aggregate root.
|
|
100
|
+
* Unique identifier of the aggregate root entity.
|
|
92
101
|
*/
|
|
93
102
|
readonly id: TId;
|
|
94
103
|
/**
|
|
95
104
|
* Version number for optimistic concurrency control.
|
|
96
105
|
* Incremented on each state change to detect concurrent modifications.
|
|
106
|
+
* This version applies to the entire aggregate, including all child entities.
|
|
97
107
|
*/
|
|
98
108
|
readonly version: Version;
|
|
99
109
|
}
|
|
@@ -254,23 +264,35 @@ interface AggregateConfig {
|
|
|
254
264
|
autoVersionBump?: boolean;
|
|
255
265
|
}
|
|
256
266
|
/**
|
|
257
|
-
* Base class for
|
|
258
|
-
*
|
|
267
|
+
* Base class for creating Aggregate Roots (Entities) without Event Sourcing.
|
|
268
|
+
*
|
|
269
|
+
* This class creates an Entity that serves as the Aggregate Root. The Aggregate Root
|
|
270
|
+
* is the parent Entity of the aggregate and represents it externally. It has identity
|
|
271
|
+
* (id) and version for optimistic concurrency control.
|
|
272
|
+
*
|
|
273
|
+
* The aggregate state (`TState`) contains:
|
|
274
|
+
* - Child entities (Entities with id, but no own version)
|
|
275
|
+
* - Value objects (immutable objects)
|
|
276
|
+
*
|
|
277
|
+
* All changes to child entities are versioned through the Aggregate Root. The version
|
|
278
|
+
* applies to the entire aggregate, including all child entities.
|
|
279
|
+
*
|
|
280
|
+
* Provides core functionality:
|
|
259
281
|
* - ID and Version management (for Optimistic Concurrency Control)
|
|
260
|
-
* - State management
|
|
282
|
+
* - State management (containing child entities and value objects)
|
|
261
283
|
* - Snapshot support for performance optimization
|
|
262
284
|
*
|
|
263
|
-
* Implements `AggregateRoot<TId>` to
|
|
264
|
-
* in Domain-Driven Design terms.
|
|
285
|
+
* Implements `AggregateRoot<TId>` to mark this as an Aggregate Root Entity.
|
|
265
286
|
*
|
|
266
287
|
* Use this class when you don't need Event Sourcing but still want
|
|
267
288
|
* aggregate patterns with versioning and state management.
|
|
268
289
|
*
|
|
269
|
-
* @template TState - The type of the aggregate state
|
|
270
|
-
* @template TId - The type of the aggregate identifier
|
|
290
|
+
* @template TState - The type of the aggregate state (contains child entities and value objects)
|
|
291
|
+
* @template TId - The type of the aggregate root identifier
|
|
271
292
|
*
|
|
272
293
|
* @example
|
|
273
294
|
* ```typescript
|
|
295
|
+
* // Order is an Aggregate Root (an Entity)
|
|
274
296
|
* class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
|
|
275
297
|
* constructor(id: OrderId, initialState: OrderState) {
|
|
276
298
|
* super(id, initialState);
|
|
@@ -278,7 +300,7 @@ interface AggregateConfig {
|
|
|
278
300
|
*
|
|
279
301
|
* confirm(): void {
|
|
280
302
|
* this._state = { ...this._state, status: "confirmed" };
|
|
281
|
-
* this.bumpVersion();
|
|
303
|
+
* this.bumpVersion(); // Versions the entire aggregate
|
|
282
304
|
* }
|
|
283
305
|
* }
|
|
284
306
|
* ```
|
|
@@ -433,6 +455,343 @@ declare function isOk<T, E>(result: Result<T, E>): result is Ok<T>;
|
|
|
433
455
|
* ```
|
|
434
456
|
*/
|
|
435
457
|
declare function isErr<T, E>(result: Result<T, E>): result is Err<E>;
|
|
458
|
+
/**
|
|
459
|
+
* Chains Result operations (flatMap/bind).
|
|
460
|
+
* If the result is Ok, applies the function to the value.
|
|
461
|
+
* If Err, returns the error unchanged.
|
|
462
|
+
*
|
|
463
|
+
* @param result - The result to chain
|
|
464
|
+
* @param fn - Function that takes the Ok value and returns a new Result
|
|
465
|
+
* @returns A new Result
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* const result = validateUserId("123")
|
|
470
|
+
* .andThen(userId => validateEmail("test@example.com")
|
|
471
|
+
* .map(email => ({ id: userId, email }))
|
|
472
|
+
* );
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
declare function andThen<T, E, U>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E>;
|
|
476
|
+
/**
|
|
477
|
+
* Transforms the Ok value using a function.
|
|
478
|
+
* If the result is Err, returns the error unchanged.
|
|
479
|
+
*
|
|
480
|
+
* @param result - The result to transform
|
|
481
|
+
* @param fn - Function to transform the Ok value
|
|
482
|
+
* @returns A new Result with transformed value
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* const result = ok(5);
|
|
487
|
+
* const doubled = map(result, x => x * 2); // Ok<10>
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
declare function map<T, E, U>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;
|
|
491
|
+
/**
|
|
492
|
+
* Transforms the Err value using a function.
|
|
493
|
+
* If the result is Ok, returns the value unchanged.
|
|
494
|
+
*
|
|
495
|
+
* @param result - The result to transform
|
|
496
|
+
* @param fn - Function to transform the Err value
|
|
497
|
+
* @returns A new Result with transformed error
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* const result = err("not found");
|
|
502
|
+
* const mapped = mapErr(result, e => `Error: ${e}`); // Err<"Error: not found">
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
505
|
+
declare function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;
|
|
506
|
+
/**
|
|
507
|
+
* Returns the value if Ok, otherwise returns the default value.
|
|
508
|
+
*
|
|
509
|
+
* @param result - The result to unwrap
|
|
510
|
+
* @param defaultValue - Default value to return if Err
|
|
511
|
+
* @returns The Ok value or the default value
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
* ```typescript
|
|
515
|
+
* const result = validateUserId("123");
|
|
516
|
+
* const userId = unwrapOr(result, "default-id");
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
declare function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T;
|
|
520
|
+
/**
|
|
521
|
+
* Returns the value if Ok, otherwise computes default from error.
|
|
522
|
+
*
|
|
523
|
+
* @param result - The result to unwrap
|
|
524
|
+
* @param fn - Function to compute default from error
|
|
525
|
+
* @returns The Ok value or computed default
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```typescript
|
|
529
|
+
* const result = validateUserId("");
|
|
530
|
+
* const userId = unwrapOrElse(result, err => `fallback-${Date.now()}`);
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
533
|
+
declare function unwrapOrElse<T, E>(result: Result<T, E>, fn: (error: E) => T): T;
|
|
534
|
+
/**
|
|
535
|
+
* Pattern matching for Result.
|
|
536
|
+
* Applies one function if Ok, another if Err.
|
|
537
|
+
*
|
|
538
|
+
* @param result - The result to match
|
|
539
|
+
* @param onOk - Function to apply if Ok
|
|
540
|
+
* @param onErr - Function to apply if Err
|
|
541
|
+
* @returns The result of applying the appropriate function
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```typescript
|
|
545
|
+
* const message = match(result,
|
|
546
|
+
* value => `Success: ${value}`,
|
|
547
|
+
* error => `Error: ${error}`
|
|
548
|
+
* );
|
|
549
|
+
* ```
|
|
550
|
+
*
|
|
551
|
+
* @example Using object syntax
|
|
552
|
+
* ```typescript
|
|
553
|
+
* const message = match(result, {
|
|
554
|
+
* ok: value => `Success: ${value}`,
|
|
555
|
+
* err: error => `Error: ${error}`
|
|
556
|
+
* });
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
declare function match<T, E, R>(result: Result<T, E>, onOk: (value: T) => R, onErr: (error: E) => R): R;
|
|
560
|
+
declare function match<T, E, R>(result: Result<T, E>, handlers: {
|
|
561
|
+
ok: (value: T) => R;
|
|
562
|
+
err: (error: E) => R;
|
|
563
|
+
}): R;
|
|
564
|
+
/**
|
|
565
|
+
* Async pattern matching for Result.
|
|
566
|
+
* Applies one async function if Ok, another if Err.
|
|
567
|
+
* Both handlers must return Promises.
|
|
568
|
+
*
|
|
569
|
+
* @param result - The result to match
|
|
570
|
+
* @param onOk - Async function to apply if Ok
|
|
571
|
+
* @param onErr - Async function to apply if Err
|
|
572
|
+
* @returns Promise resolving to the result of applying the appropriate function
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```typescript
|
|
576
|
+
* const message = await matchAsync(result,
|
|
577
|
+
* async (value) => `Success: ${value}`,
|
|
578
|
+
* async (error) => `Error: ${error}`
|
|
579
|
+
* );
|
|
580
|
+
* ```
|
|
581
|
+
*
|
|
582
|
+
* @example Using object syntax
|
|
583
|
+
* ```typescript
|
|
584
|
+
* const message = await matchAsync(result, {
|
|
585
|
+
* ok: async (value) => `Success: ${value}`,
|
|
586
|
+
* err: async (error) => `Error: ${error}`
|
|
587
|
+
* });
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
declare function matchAsync<T, E, R>(result: Result<T, E>, onOk: (value: T) => Promise<R>, onErr: (error: E) => Promise<R>): Promise<R>;
|
|
591
|
+
declare function matchAsync<T, E, R>(result: Result<T, E>, handlers: {
|
|
592
|
+
ok: (value: T) => Promise<R>;
|
|
593
|
+
err: (error: E) => Promise<R>;
|
|
594
|
+
}): Promise<R>;
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Base class for Result with method chaining support.
|
|
598
|
+
* Provides common methods for both Ok and Err classes.
|
|
599
|
+
*/
|
|
600
|
+
declare abstract class ResultBase<T, E> {
|
|
601
|
+
protected readonly _result: Result<T, E>;
|
|
602
|
+
protected constructor(result: Result<T, E>);
|
|
603
|
+
/**
|
|
604
|
+
* Returns the value if Ok, otherwise throws an error.
|
|
605
|
+
* If error is an Error instance, throws it directly.
|
|
606
|
+
* Otherwise, wraps the error in a new Error.
|
|
607
|
+
*
|
|
608
|
+
* @throws Error if the result is Err
|
|
609
|
+
*/
|
|
610
|
+
unwrap(): T;
|
|
611
|
+
/**
|
|
612
|
+
* Returns the value if Ok, otherwise returns the default value.
|
|
613
|
+
*/
|
|
614
|
+
unwrapOr(defaultValue: T): T;
|
|
615
|
+
/**
|
|
616
|
+
* Returns the value if Ok, otherwise computes default from error.
|
|
617
|
+
*/
|
|
618
|
+
unwrapOrElse(fn: (error: E) => T): T;
|
|
619
|
+
/**
|
|
620
|
+
* Pattern matching for Result.
|
|
621
|
+
* Applies one function if Ok, another if Err.
|
|
622
|
+
*
|
|
623
|
+
* @example
|
|
624
|
+
* ```typescript
|
|
625
|
+
* outcome.match(
|
|
626
|
+
* value => `Success: ${value}`,
|
|
627
|
+
* error => `Error: ${error}`
|
|
628
|
+
* );
|
|
629
|
+
* ```
|
|
630
|
+
*
|
|
631
|
+
* @example Using object syntax
|
|
632
|
+
* ```typescript
|
|
633
|
+
* outcome.match({
|
|
634
|
+
* ok: value => `Success: ${value}`,
|
|
635
|
+
* err: error => `Error: ${error}`
|
|
636
|
+
* });
|
|
637
|
+
* ```
|
|
638
|
+
*/
|
|
639
|
+
match<R>(onOk: (value: T) => R, onErr: (error: E) => R): R;
|
|
640
|
+
match<R>(handlers: {
|
|
641
|
+
ok: (value: T) => R;
|
|
642
|
+
err: (error: E) => R;
|
|
643
|
+
}): R;
|
|
644
|
+
/**
|
|
645
|
+
* Async pattern matching for Result.
|
|
646
|
+
* Applies one async function if Ok, another if Err.
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* ```typescript
|
|
650
|
+
* await outcome.matchAsync(
|
|
651
|
+
* async (value) => `Success: ${value}`,
|
|
652
|
+
* async (error) => `Error: ${error}`
|
|
653
|
+
* );
|
|
654
|
+
* ```
|
|
655
|
+
*
|
|
656
|
+
* @example Using object syntax
|
|
657
|
+
* ```typescript
|
|
658
|
+
* await outcome.matchAsync({
|
|
659
|
+
* ok: async (value) => `Success: ${value}`,
|
|
660
|
+
* err: async (error) => `Error: ${error}`
|
|
661
|
+
* });
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
matchAsync<R>(onOk: (value: T) => Promise<R>, onErr: (error: E) => Promise<R>): Promise<R>;
|
|
665
|
+
matchAsync<R>(handlers: {
|
|
666
|
+
ok: (value: T) => Promise<R>;
|
|
667
|
+
err: (error: E) => Promise<R>;
|
|
668
|
+
}): Promise<R>;
|
|
669
|
+
/**
|
|
670
|
+
* Type guard to check if the result is Ok.
|
|
671
|
+
*/
|
|
672
|
+
isOk(): this is Success<T>;
|
|
673
|
+
/**
|
|
674
|
+
* Type guard to check if the result is Err.
|
|
675
|
+
*/
|
|
676
|
+
isErr(): this is Erroneous<E>;
|
|
677
|
+
/**
|
|
678
|
+
* Gets the underlying Result value.
|
|
679
|
+
*/
|
|
680
|
+
get result(): Result<T, E>;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Class representing a successful result with method chaining support.
|
|
684
|
+
* Use this for class-based API with method chaining.
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* ```typescript
|
|
688
|
+
* const okResult = Ok(1);
|
|
689
|
+
* const doubled = okResult.map(x => x * 2).unwrap(); // 2
|
|
690
|
+
*
|
|
691
|
+
* const chained = Ok(5)
|
|
692
|
+
* .andThen(x => Ok(x * 2))
|
|
693
|
+
* .map(x => x + 1)
|
|
694
|
+
* .unwrap(); // 11
|
|
695
|
+
* ```
|
|
696
|
+
*/
|
|
697
|
+
declare class Success<T> extends ResultBase<T, never> {
|
|
698
|
+
constructor(value: T);
|
|
699
|
+
/**
|
|
700
|
+
* Factory function to create an Ok instance.
|
|
701
|
+
* Can be called with or without `new`.
|
|
702
|
+
*/
|
|
703
|
+
static of<T>(value: T): Success<T>;
|
|
704
|
+
/**
|
|
705
|
+
* Chains Result operations (flatMap/bind).
|
|
706
|
+
* If the result is Ok, applies the function to the value.
|
|
707
|
+
* If Err, returns the error unchanged.
|
|
708
|
+
*/
|
|
709
|
+
andThen<U, E>(fn: (value: T) => Result<U, E>): Success<U> | Erroneous<E>;
|
|
710
|
+
/**
|
|
711
|
+
* Transforms the Ok value using a function.
|
|
712
|
+
* If the result is Err, returns the error unchanged.
|
|
713
|
+
*/
|
|
714
|
+
map<U>(fn: (value: T) => U): Success<U>;
|
|
715
|
+
/**
|
|
716
|
+
* Transforms the Err value using a function.
|
|
717
|
+
* If the result is Ok, returns the value unchanged.
|
|
718
|
+
*/
|
|
719
|
+
mapErr<F>(_fn: (error: never) => F): Success<T>;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Class representing an erroneous result with method chaining support.
|
|
724
|
+
* Use this for class-based API with method chaining.
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* const errResult = Err(new Error("error"));
|
|
729
|
+
* errResult.map(x => x * 2).unwrap(); // throws Error("error")
|
|
730
|
+
*
|
|
731
|
+
* const mapped = Err("original")
|
|
732
|
+
* .mapErr(e => `Error: ${e}`)
|
|
733
|
+
* .unwrap(); // throws Error("Error: original")
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
declare class Erroneous<E> extends ResultBase<never, E> {
|
|
737
|
+
constructor(error: E);
|
|
738
|
+
/**
|
|
739
|
+
* Factory function to create an Err instance.
|
|
740
|
+
* Can be called with or without `new`.
|
|
741
|
+
*/
|
|
742
|
+
static of<E>(error: E): Erroneous<E>;
|
|
743
|
+
/**
|
|
744
|
+
* Chains Result operations (flatMap/bind).
|
|
745
|
+
* If the result is Ok, applies the function to the value.
|
|
746
|
+
* If Err, returns the error unchanged.
|
|
747
|
+
*/
|
|
748
|
+
andThen<U>(_fn: (value: never) => Result<U, E>): Erroneous<E>;
|
|
749
|
+
/**
|
|
750
|
+
* Transforms the Ok value using a function.
|
|
751
|
+
* If the result is Err, returns the error unchanged.
|
|
752
|
+
*/
|
|
753
|
+
map<U>(_fn: (value: never) => U): Erroneous<E>;
|
|
754
|
+
/**
|
|
755
|
+
* Transforms the Err value using a function.
|
|
756
|
+
* If the result is Ok, returns the value unchanged.
|
|
757
|
+
*/
|
|
758
|
+
mapErr<F>(fn: (error: E) => F): Erroneous<F>;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Class-based API for Result with method chaining support.
|
|
763
|
+
* Provides an object-oriented alternative to the functional Result type.
|
|
764
|
+
* Use `Outcome.from()` to wrap an existing Result, or `Outcome.ok()` / `Outcome.err()` to create new instances.
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```typescript
|
|
768
|
+
* // Wrap an existing Result
|
|
769
|
+
* const result = Outcome.from(ok(1));
|
|
770
|
+
* const doubled = result.map(x => x * 2).unwrap(); // 2
|
|
771
|
+
*
|
|
772
|
+
* // Create directly
|
|
773
|
+
* const outcome = Outcome.ok(42);
|
|
774
|
+
* const value = outcome.map(x => x + 1).unwrap(); // 43
|
|
775
|
+
* ```
|
|
776
|
+
*/
|
|
777
|
+
declare class Outcome<T, E> extends ResultBase<T, E> {
|
|
778
|
+
private constructor();
|
|
779
|
+
/**
|
|
780
|
+
* Creates an Outcome from an Ok value.
|
|
781
|
+
*/
|
|
782
|
+
static ok<T>(value: T): Success<T>;
|
|
783
|
+
/**
|
|
784
|
+
* Creates an Outcome from an Err value.
|
|
785
|
+
*/
|
|
786
|
+
static err<E>(error: E): Erroneous<E>;
|
|
787
|
+
/**
|
|
788
|
+
* Creates an Outcome from a Result.
|
|
789
|
+
*/
|
|
790
|
+
static from<T, E>(result: Result<T, E>): Outcome<T, E>;
|
|
791
|
+
andThen<U>(fn: (value: T) => Result<U, E>): Outcome<U, E>;
|
|
792
|
+
map<U>(fn: (value: T) => U): Outcome<U, E>;
|
|
793
|
+
mapErr<F>(fn: (error: E) => F): Outcome<T, F>;
|
|
794
|
+
}
|
|
436
795
|
|
|
437
796
|
type Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;
|
|
438
797
|
/**
|
|
@@ -446,7 +805,18 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
446
805
|
autoVersionBump?: boolean;
|
|
447
806
|
}
|
|
448
807
|
/**
|
|
449
|
-
* Base class for Event-Sourced
|
|
808
|
+
* Base class for Event-Sourced Aggregate Roots (Entities).
|
|
809
|
+
*
|
|
810
|
+
* Extends `AggregateBase` to create an Aggregate Root Entity with Event Sourcing capabilities.
|
|
811
|
+
* The Aggregate Root is the parent Entity of the aggregate and represents it externally.
|
|
812
|
+
*
|
|
813
|
+
* The aggregate state (`TState`) contains:
|
|
814
|
+
* - Child entities (Entities with id, but no own version)
|
|
815
|
+
* - Value objects (immutable objects)
|
|
816
|
+
*
|
|
817
|
+
* All changes to child entities are versioned through the Aggregate Root. The version
|
|
818
|
+
* applies to the entire aggregate, including all child entities.
|
|
819
|
+
*
|
|
450
820
|
* Extends `AggregateBase` with Event Sourcing capabilities:
|
|
451
821
|
* - Event tracking (pendingEvents)
|
|
452
822
|
* - Event handlers for state transitions
|
|
@@ -456,12 +826,13 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
456
826
|
* Use this class when you want Event Sourcing with full event tracking
|
|
457
827
|
* and replay capabilities.
|
|
458
828
|
*
|
|
459
|
-
* @template TState - The type of the aggregate state
|
|
829
|
+
* @template TState - The type of the aggregate state (contains child entities and value objects)
|
|
460
830
|
* @template TEvent - The union type of all domain events
|
|
461
|
-
* @template TId - The type of the aggregate identifier
|
|
831
|
+
* @template TId - The type of the aggregate root identifier
|
|
462
832
|
*
|
|
463
833
|
* @example
|
|
464
834
|
* ```typescript
|
|
835
|
+
* // Order is an Aggregate Root (an Entity) with Event Sourcing
|
|
465
836
|
* class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
|
|
466
837
|
* confirm(): void {
|
|
467
838
|
* this.apply(createDomainEvent("OrderConfirmed", {}));
|
|
@@ -957,6 +1328,11 @@ interface IQueryBus {
|
|
|
957
1328
|
*/
|
|
958
1329
|
register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
|
|
959
1330
|
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Type map for query types to their return types.
|
|
1333
|
+
* Used to improve type inference in QueryBus.
|
|
1334
|
+
*/
|
|
1335
|
+
type QueryTypeMap = Record<string, unknown>;
|
|
960
1336
|
/**
|
|
961
1337
|
* Simple in-memory query bus implementation.
|
|
962
1338
|
* Handlers are stored in a Map and dispatched based on query type.
|
|
@@ -983,9 +1359,12 @@ interface IQueryBus {
|
|
|
983
1359
|
* const order = await bus.execute({ type: "GetOrder", orderId: "123" });
|
|
984
1360
|
* ```
|
|
985
1361
|
*/
|
|
986
|
-
declare class QueryBus implements IQueryBus {
|
|
1362
|
+
declare class QueryBus<TMap extends QueryTypeMap = QueryTypeMap> implements IQueryBus {
|
|
987
1363
|
private readonly handlers;
|
|
988
1364
|
register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
|
|
1365
|
+
execute<Q extends Query & {
|
|
1366
|
+
type: keyof TMap;
|
|
1367
|
+
}>(query: Q): Promise<Result<TMap[Q["type"]], string>>;
|
|
989
1368
|
execute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;
|
|
990
1369
|
executeUnsafe<Q extends Query, R>(query: Q): Promise<R>;
|
|
991
1370
|
}
|
|
@@ -1009,18 +1388,42 @@ declare class QueryBus implements IQueryBus {
|
|
|
1009
1388
|
declare function guard(cond: boolean, error: string): Result<true, string>;
|
|
1010
1389
|
|
|
1011
1390
|
/**
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1391
|
+
* Interface for entities with identity.
|
|
1392
|
+
*
|
|
1393
|
+
* In Domain-Driven Design, there are two types of entities:
|
|
1394
|
+
*
|
|
1395
|
+
* 1. **Aggregate Root Entity**: The parent Entity of an aggregate.
|
|
1396
|
+
* - Has identity (id) and version
|
|
1397
|
+
* - Implemented by classes extending `AggregateBase` or `AggregateEventSourced`
|
|
1398
|
+
* - Represents the aggregate externally
|
|
1399
|
+
* - Loaded/saved through repositories
|
|
1400
|
+
*
|
|
1401
|
+
* 2. **Child Entities**: Entities within an aggregate.
|
|
1402
|
+
* - Have identity (id), but no own version
|
|
1403
|
+
* - Exist only within the aggregate boundary
|
|
1404
|
+
* - Versioned through the Aggregate Root
|
|
1405
|
+
* - Cannot be referenced directly from outside the aggregate
|
|
1406
|
+
*
|
|
1407
|
+
* This interface is used for child entities within aggregates. The Aggregate Root
|
|
1408
|
+
* also implements this interface (through `AggregateRoot<TId>`), but additionally
|
|
1409
|
+
* has version management.
|
|
1015
1410
|
*
|
|
1016
1411
|
* @template TId - The type of the entity identifier
|
|
1017
1412
|
*
|
|
1018
1413
|
* @example
|
|
1019
1414
|
* ```typescript
|
|
1415
|
+
* // Child Entity within an aggregate
|
|
1020
1416
|
* type OrderItem = Entity<ItemId> & {
|
|
1021
1417
|
* productId: string;
|
|
1022
1418
|
* quantity: number;
|
|
1023
1419
|
* };
|
|
1420
|
+
*
|
|
1421
|
+
* // Aggregate Root (also an Entity, but with version)
|
|
1422
|
+
* class Order extends AggregateBase<OrderState, OrderId>
|
|
1423
|
+
* implements AggregateRoot<OrderId> {
|
|
1424
|
+
* // Order is an Entity (the Aggregate Root)
|
|
1425
|
+
* // OrderState contains OrderItem (child entities)
|
|
1426
|
+
* }
|
|
1024
1427
|
* ```
|
|
1025
1428
|
*/
|
|
1026
1429
|
interface Entity<TId> {
|
|
@@ -1222,16 +1625,23 @@ interface ISpecification<T> {
|
|
|
1222
1625
|
}
|
|
1223
1626
|
|
|
1224
1627
|
/**
|
|
1225
|
-
*
|
|
1226
|
-
* It encapsulates the complexity of the data source (DB, API, etc.).
|
|
1628
|
+
* Repository interface for Aggregate Roots (Entities).
|
|
1227
1629
|
*
|
|
1228
|
-
* Repositories work with Aggregate
|
|
1229
|
-
*
|
|
1630
|
+
* Repositories work exclusively with Aggregate Root Entities. The Aggregate Root
|
|
1631
|
+
* is the Entity that represents the aggregate externally and is the only object
|
|
1632
|
+
* that can be loaded/saved through repositories.
|
|
1230
1633
|
*
|
|
1231
|
-
*
|
|
1634
|
+
* When loading an Aggregate Root, all child entities and value objects within
|
|
1635
|
+
* the aggregate state are loaded as well. When saving, the entire aggregate
|
|
1636
|
+
* (including all child entities) is persisted as a unit.
|
|
1637
|
+
*
|
|
1638
|
+
* Child entities cannot be loaded or saved independently - they exist only
|
|
1639
|
+
* within the aggregate boundary and are managed through the Aggregate Root.
|
|
1640
|
+
*
|
|
1641
|
+
* @template TState - The type of the aggregate state (contains child entities and value objects)
|
|
1232
1642
|
* @template TEvent - The union type of all domain events
|
|
1233
|
-
* @template TAgg - The aggregate type (must be an Aggregate Root)
|
|
1234
|
-
* @template TId - The type of the aggregate identifier
|
|
1643
|
+
* @template TAgg - The aggregate root type (must be an Aggregate Root Entity)
|
|
1644
|
+
* @template TId - The type of the aggregate root identifier
|
|
1235
1645
|
*/
|
|
1236
1646
|
interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends AggregateRoot<TId> & Aggregate<TState, TEvent>, TId extends Id<string>> {
|
|
1237
1647
|
getById(id: TId): Promise<TAgg | null>;
|
|
@@ -1325,4 +1735,4 @@ declare function voWithValidation<T>(t: T, validate: (value: T) => boolean, erro
|
|
|
1325
1735
|
*/
|
|
1326
1736
|
declare function voWithValidationUnsafe<T>(t: T, validate: (value: T) => boolean, errorMessage?: string): ValueObject<T>;
|
|
1327
1737
|
|
|
1328
|
-
export { type Aggregate, AggregateBase, type AggregateConfig, AggregateEventSourced, type AggregateEventSourcedConfig, type AggregateRoot, type AggregateSnapshot, type Clock, type Command, CommandBus, type CommandHandler, type DomainEvent, type Entity, type Err, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, type ICommandBus, type IQueryBus, type IRepository, type ISpecification, type Id, type IdGenerator, type Ok, type Outbox, type Query, QueryBus, type QueryHandler, type RepoProvider, type Result, type UnitOfWork, type ValueObject, type Version, aggregate, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, entityIds, err, findEntityById, guard, hasEntityId, isErr, isOk, mergeMetadata, ok, removeEntityById, replaceEntityById, sameAggregate, sameEntity, updateEntityById, vo, voEquals, voWithValidation, voWithValidationUnsafe, withCommit, withEvent };
|
|
1738
|
+
export { type Aggregate, AggregateBase, type AggregateConfig, AggregateEventSourced, type AggregateEventSourcedConfig, type AggregateRoot, type AggregateSnapshot, type Clock, type Command, CommandBus, type CommandHandler, type DomainEvent, type Entity, type Err, Erroneous, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, type ICommandBus, type IQueryBus, type IRepository, type ISpecification, type Id, type IdGenerator, type Ok, type Outbox, Outcome, type Query, QueryBus, type QueryHandler, type RepoProvider, type Result, Success, type UnitOfWork, type ValueObject, type Version, aggregate, andThen, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, entityIds, err, findEntityById, guard, hasEntityId, isErr, isOk, map, mapErr, match, matchAsync, mergeMetadata, ok, removeEntityById, replaceEntityById, sameAggregate, sameEntity, unwrapOr, unwrapOrElse, updateEntityById, vo, voEquals, voWithValidation, voWithValidationUnsafe, withCommit, withEvent };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var _=Object.defineProperty;var n=(t,e)=>_(t,"name",{value:e,configurable:true});function V(t,e=0){return {state:t,version:e,pendingEvents:[]}}n(V,"aggregate");function Q(t,e){return {...t,pendingEvents:[...t.pendingEvents,e]}}n(Q,"withEvent");function C(t){return {...t,version:t.version+1}}n(C,"bump");function A(t,e,r){return {type:t,payload:e,occurredAt:r?.occurredAt??new Date,version:r?.version??1,metadata:r?.metadata}}n(A,"createDomainEvent");function U(t,e,r,o){return A(t,e,{...o,metadata:r})}n(U,"createDomainEventWithMetadata");function O(t,e){return {...t.metadata??{},...e??{}}}n(O,"copyMetadata");function B(...t){return Object.assign({},...t.filter(Boolean))}n(B,"mergeMetadata");function M(t,e){return t.id===e.id&&t.version===e.version}n(M,"sameAggregate");var d=class{static{n(this,"AggregateBase");}id;version=0;_config;_autoVersionBump;get state(){return this._state}_state;constructor(e,r,o){this.id=e,this._state=r,this._config=o??{},this._autoVersionBump=this._config.autoVersionBump??false;}bumpVersion(){this.version=this.version+1;}setState(e,r){this._state=e,(r??this._autoVersionBump)&&this.bumpVersion();}createSnapshot(){return {state:{...this._state},version:this.version,snapshotAt:new Date}}restoreFromSnapshot(e){this._state=e.state,this.version=e.version;}};function a(t){return {ok:true,value:t}}n(a,"ok");function i(t){return {ok:false,error:t}}n(i,"err");function v(t){return t.ok===true}n(v,"isOk");function g(t){return t.ok===false}n(g,"isErr");function l(t,e){return t.ok?e(t.value):t}n(l,"andThen");function c(t,e){return t.ok?a(e(t.value)):t}n(c,"map");function E(t,e){return t.ok?t:i(e(t.error))}n(E,"mapErr");function h(t,e){return t.ok?t.value:e}n(h,"unwrapOr");function y(t,e){return t.ok?t.value:e(t.error)}n(y,"unwrapOrElse");function T(t,e,r){return typeof e=="function"?t.ok?e(t.value):r(t.error):t.ok?e.ok(t.value):e.err(t.error)}n(T,"match");async function m(t,e,r){return typeof e=="function"?t.ok?e(t.value):r(t.error):t.ok?e.ok(t.value):e.err(t.error)}n(m,"matchAsync");var u=class{static{n(this,"ResultBase");}_result;constructor(e){this._result=e;}unwrap(){if(this._result.ok)return this._result.value;throw this._result.error instanceof Error?this._result.error:new Error(String(this._result.error))}unwrapOr(e){return h(this._result,e)}unwrapOrElse(e){return y(this._result,e)}match(e,r){return typeof e=="function"?T(this._result,e,r):T(this._result,e)}async matchAsync(e,r){return typeof e=="function"?m(this._result,e,r):m(this._result,e)}isOk(){return v(this._result)}isErr(){return g(this._result)}get result(){return this._result}},f=class t extends u{static{n(this,"Success");}constructor(e){super(a(e));}static of(e){return new t(e)}andThen(e){let r=l(this._result,e);return r.ok?new t(r.value):new p(r.error)}map(e){let r=c(this._result,e);if(r.ok)return new t(r.value);throw new Error("Unexpected error in Success.map")}mapErr(e){return this}};var p=class t extends u{static{n(this,"Erroneous");}constructor(e){super(i(e));}static of(e){return new t(e)}andThen(e){return this}map(e){return this}mapErr(e){let r=E(this._result,e);if(!r.ok)return new t(r.error);throw new Error("Unexpected ok in Erroneous.mapErr")}};var x=class t extends u{static{n(this,"Outcome");}constructor(e){super(e);}static ok(e){return new f(e)}static err(e){return new p(e)}static from(e){return new t(e)}andThen(e){return t.from(l(this._result,e))}map(e){return t.from(c(this._result,e))}mapErr(e){return t.from(E(this._result,e))}};var R=class extends d{static{n(this,"AggregateEventSourced");}_eventConfig;_eventAutoVersionBump;_pendingEvents=[];constructor(e,r,o){super(e,r,o),this._eventConfig=o??{},this._eventAutoVersionBump=this._eventConfig.autoVersionBump??true;}get pendingEvents(){return this._pendingEvents}clearPendingEvents(){this._pendingEvents.length=0;}validateEvent(e){return a(true)}apply(e,r=true){let o=this.validateEvent(e);if(!o.ok)return i(`Event validation failed for ${e.type}: ${o.error}`);let s=this.handlers[e.type];return s?(this._state=s(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1)),a()):i(`Missing handler for event type: ${e.type}`)}applyUnsafe(e,r=true){let o=this.validateEvent(e);if(!o.ok)throw new Error(`Event validation failed for ${e.type}: ${o.error}`);let s=this.handlers[e.type];if(!s)throw new Error(`Missing handler for event type: ${e.type}`);this._state=s(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1));}bumpVersion(){this.version=this.version+1;}loadFromHistory(e){for(let r of e){let o=this.apply(r,false);if(!o.ok)return o}return this.version=e.length,a()}hasPendingEvents(){return this._pendingEvents.length>0}getEventCount(){return this._pendingEvents.length}getLatestEvent(){return this._pendingEvents[this._pendingEvents.length-1]}restoreFromSnapshotWithEvents(e,r){this._state=e.state,this.version=e.version;for(let o of r){let s=this.apply(o,false);if(!s.ok)return s}return this.version=e.version+r.length,a()}};var k=class{static{n(this,"CommandBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);return r?r(e):i(`No handler registered for command type: ${e.type}`)}};function ne(t,e){return t.uow.transactional(async()=>{let{result:r,events:o}=await e();return await t.outbox.add(o),t.bus&&await t.bus.publish(o),r})}n(ne,"withCommit");var w=class{static{n(this,"QueryBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);if(!r)return i(`No handler registered for query type: ${e.type}`);try{let o=await r(e);return a(o)}catch(o){return i(o instanceof Error?o.message:String(o))}}async executeUnsafe(e){let r=this.handlers.get(e.type);if(!r)throw new Error(`No handler registered for query type: ${e.type}`);return r(e)}};function de(t,e){return t?a(true):i(e)}n(de,"guard");function Ee(t,e){return t.id===e.id}n(Ee,"sameEntity");function Te(t,e){return t.find(r=>r.id===e)}n(Te,"findEntityById");function me(t,e){return t.some(r=>r.id===e)}n(me,"hasEntityId");function fe(t,e){return t.filter(r=>r.id!==e)}n(fe,"removeEntityById");function ve(t,e,r){return t.map(o=>o.id===e?r(o):o)}n(ve,"updateEntityById");function ge(t,e,r){return t.map(o=>o.id===e?r:o)}n(ge,"replaceEntityById");function he(t){return t.map(e=>e.id)}n(he,"entityIds");var S=class{static{n(this,"EventBusImpl");}handlers=new Map;subscribe(e,r){let o=e;this.handlers.has(o)||this.handlers.set(o,new Set);let s=this.handlers.get(o);return s.add(r),()=>{s.delete(r),s.size===0&&this.handlers.delete(o);}}async publish(e){for(let r of e){let o=this.handlers.get(r.type);o&&await Promise.all(Array.from(o).map(s=>s(r)));}}};function b(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t))return t;e.add(t);let r=Object.getOwnPropertyNames(t);for(let o of r){let s=t[o];s&&(typeof s=="object"||Array.isArray(s))&&b(s,e);}return Object.freeze(t)}n(b,"deepFreeze");function I(t){return b({...t})}n(I,"vo");function Se(t,e){return JSON.stringify(t)===JSON.stringify(e)}n(Se,"voEquals");function be(t,e,r){return e(t)?a(I(t)):i(r??`Validation failed for value object: ${JSON.stringify(t)}`)}n(be,"voWithValidation");function Ie(t,e,r){if(!e(t))throw new Error(r??`Validation failed for value object: ${JSON.stringify(t)}`);return I(t)}n(Ie,"voWithValidationUnsafe");export{d as AggregateBase,R as AggregateEventSourced,k as CommandBus,p as Erroneous,S as EventBusImpl,x as Outcome,w as QueryBus,f as Success,V as aggregate,l as andThen,C as bump,O as copyMetadata,A as createDomainEvent,U as createDomainEventWithMetadata,he as entityIds,i as err,Te as findEntityById,de as guard,me as hasEntityId,g as isErr,v as isOk,c as map,E as mapErr,T as match,m as matchAsync,B as mergeMetadata,a as ok,fe as removeEntityById,ge as replaceEntityById,M as sameAggregate,Ee as sameEntity,h as unwrapOr,y as unwrapOrElse,ve as updateEntityById,I as vo,Se as voEquals,be as voWithValidation,Ie as voWithValidationUnsafe,ne as withCommit,Q as withEvent};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|