@rineex/ddd 1.6.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -197,7 +197,7 @@ interface DomainErrorMetadata {
197
197
  * }
198
198
  * }
199
199
  */
200
- declare abstract class DomainError extends Error {
200
+ declare abstract class DomainError$1 extends Error {
201
201
  /** Stable, machine-readable error code */
202
202
  readonly code: string;
203
203
  /** Structured, immutable domain metadata */
@@ -310,11 +310,11 @@ declare abstract class ValueObject<T> {
310
310
  /**
311
311
  * Custom error class for entity validation failures.
312
312
  */
313
- declare class EntityValidationError extends DomainError {
313
+ declare class EntityValidationError extends DomainError$1 {
314
314
  constructor(message: string, cause?: Error);
315
315
  }
316
316
 
317
- declare class InvalidValueObjectError extends DomainError {
317
+ declare class InvalidValueObjectError extends DomainError$1 {
318
318
  constructor(message: string);
319
319
  }
320
320
 
@@ -522,138 +522,359 @@ declare const HttpStatusMessage: {
522
522
  type HttpStatusMessage = (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];
523
523
 
524
524
  /**
525
- * Base class for Domain violations.
526
- * Purposely does NOT extend native Error to avoid stack trace overhead in the domain.
525
+ * Strongly-typed domain error used in Result failures.
526
+ * Does NOT extend native Error on purpose infrastructure maps to transport later.
527
527
  */
528
- declare abstract class DomainViolation {
529
- abstract readonly code: string;
530
- abstract readonly message: string;
531
- readonly metadata: Readonly<Record<string, unknown>>;
532
- protected constructor(metadata?: Record<string, unknown>);
528
+ declare abstract class DomainError {
529
+ abstract readonly code: DomainErrorCode;
530
+ readonly message: string;
531
+ readonly metadata: Readonly<Record<string, boolean | number | string>>;
532
+ protected constructor(params: {
533
+ message: string;
534
+ metadata?: Record<string, boolean | number | string>;
535
+ });
533
536
  }
537
+ type DomainErrorCode = 'DOMAIN.INVALID_STATE' | 'DOMAIN.INVALID_VALUE';
534
538
 
535
539
  /**
536
- * Represents the outcome of an operation that can either succeed or fail,
537
- * without relying on exceptions for control flow.
540
+ * Represents the result of an operation, which can be either a success or a failure.
538
541
  *
539
- * This pattern is commonly used in domain and application layers to make
540
- * success and failure states explicit, predictable, and type-safe.
542
+ * This is a functional programming pattern that helps avoid throwing exceptions
543
+ * and makes error handling explicit in the type system. It's commonly used in
544
+ * Domain-Driven Design (DDD) to represent domain operation outcomes.
541
545
  *
542
- * ## Design guarantees
543
- * - A `Result` is **immutable** once created.
544
- * - A `Result` is **exactly one of**:
545
- * - Success → contains a value
546
- * - Failure → contains an error
547
- * - Accessing the wrong side throws immediately (fail-fast).
546
+ * @template T The type of a successful result.
547
+ * @template E The type of the error in case of failure (defaults to DomainError).
548
548
  *
549
- * @typeParam T - Type of the success value
550
- * @typeParam E - Type of the failure error
549
+ * @example
550
+ * ```typescript
551
+ * // Creating a successful result
552
+ * const success = Result.ok({ id: 1, name: 'John' });
553
+ * if (success.isSuccess) {
554
+ * const user = success.getValue(); // { id: 1, name: 'John' }
555
+ * }
556
+ *
557
+ * // Creating a failed result
558
+ * const failure = Result.fail(new InvalidUserError('User not found'));
559
+ * if (failure.isFailure) {
560
+ * const error = failure.getError(); // InvalidUserError instance
561
+ * }
562
+ * ```
551
563
  *
552
564
  * @example
553
- * ```ts
554
- * function parseNumber(input: string): Result<number, string> {
555
- * const value = Number(input);
565
+ * ```typescript
566
+ * // Using in a domain service
567
+ * function createUser(name: string): Result<User, DomainError> {
568
+ * if (!name || name.trim().length === 0) {
569
+ * return Result.fail(new InvalidValueError('Name cannot be empty'));
570
+ * }
556
571
  *
557
- * if (Number.isNaN(value)) {
558
- * return Result.fail('Invalid number');
572
+ * const user = new User(name);
573
+ * return Result.ok(user);
574
+ * }
575
+ *
576
+ * const result = createUser('John Doe');
577
+ * if (result.isSuccess) {
578
+ * console.log('User created:', result.getValue());
579
+ * } else {
580
+ * console.error('Failed:', result.getError()?.message);
581
+ * }
582
+ * ```
583
+ *
584
+ * @example
585
+ * ```typescript
586
+ * // Chaining operations
587
+ * function validateEmail(email: string): Result<string, DomainError> {
588
+ * if (!email.includes('@')) {
589
+ * return Result.fail(new InvalidValueError('Invalid email format'));
590
+ * }
591
+ * return Result.ok(email);
592
+ * }
593
+ *
594
+ * function createAccount(email: string): Result<Account, DomainError> {
595
+ * const emailResult = validateEmail(email);
596
+ * if (emailResult.isFailure) {
597
+ * return emailResult; // Forward the error
559
598
  * }
560
599
  *
561
- * return Result.ok(value);
600
+ * const account = new Account(emailResult.getValue()!);
601
+ * return Result.ok(account);
562
602
  * }
603
+ * ```
563
604
  *
564
- * const result = parseNumber('42');
605
+ * @example
606
+ * ```typescript
607
+ * // Working with async operations
608
+ * async function fetchUser(id: number): Promise<Result<User, DomainError>> {
609
+ * try {
610
+ * const user = await userRepository.findById(id);
611
+ * if (!user) {
612
+ * return Result.fail(new NotFoundError(`User ${id} not found`));
613
+ * }
614
+ * return Result.ok(user);
615
+ * } catch (error) {
616
+ * return Result.fail(new SystemError('Database connection failed'));
617
+ * }
618
+ * }
565
619
  *
620
+ * const result = await fetchUser(123);
566
621
  * if (result.isSuccess) {
567
- * console.log(result.getValue()); // 42
622
+ * // Handle success
568
623
  * } else {
569
- * console.error(result.getError());
624
+ * // Handle failure
570
625
  * }
571
626
  * ```
572
627
  */
573
628
  declare class Result<T, E> {
574
- private readonly _value?;
575
- private readonly _error?;
576
629
  /**
577
- * Indicates whether the result represents a failed outcome.
630
+ * Indicates if the result is a failure.
578
631
  *
579
- * This flag is mutually exclusive with {@link isSuccess}.
632
+ * @example
633
+ * ```typescript
634
+ * const result = Result.fail(new Error('Something went wrong'));
635
+ * if (result.isFailure) {
636
+ * // Handle error case
637
+ * console.error(result.getError());
638
+ * }
639
+ * ```
580
640
  */
581
641
  readonly isFailure: boolean;
582
642
  /**
583
- * Indicates whether the result represents a successful outcome.
643
+ * Indicates if the result is a success.
584
644
  *
585
- * This flag is mutually exclusive with {@link isFailure}.
645
+ * @example
646
+ * ```typescript
647
+ * const result = Result.ok(42);
648
+ * if (result.isSuccess) {
649
+ * // Handle success case
650
+ * const value = result.getValue(); // 42
651
+ * }
652
+ * ```
586
653
  */
587
654
  readonly isSuccess: boolean;
588
655
  /**
589
- * Creates a new {@link Result} instance.
590
- *
591
- * This constructor is private to enforce the use of
592
- * {@link Result.ok} and {@link Result.fail} factory methods,
593
- * preserving the success/failure invariants.
594
- *
595
- * @param _value - Success value (defined only for success results)
596
- * @param _error - Failure error (defined only for failure results)
656
+ * The error, if any.
657
+ * @private
658
+ */
659
+ private readonly _error?;
660
+ /**
661
+ * The value, if any.
662
+ * @private
663
+ */
664
+ private readonly _value?;
665
+ /**
666
+ * Private constructor to enforce the use of static methods.
667
+ * @param params.value The value on success.
668
+ * @param params.error The error on failure.
669
+ * @private
597
670
  */
598
671
  private constructor();
599
672
  /**
600
- * Creates a failed {@link Result}.
673
+ * Creates a failed result.
601
674
  *
602
- * @param error - Error describing the failure
603
- * @returns A failure {@link Result} containing the provided error
675
+ * @template T The type of a successful result (never for failure).
676
+ * @template E The type of the error (defaults to DomainError).
677
+ * @param error The error object.
678
+ * @returns {Result<T, E>} A failed Result instance.
604
679
  *
605
680
  * @example
606
- * ```ts
607
- * return Result.fail(new ValidationError('Email is invalid'));
681
+ * ```typescript
682
+ * // With DomainError
683
+ * class InvalidValueError extends DomainError {
684
+ * public readonly code = 'DOMAIN.INVALID_VALUE' as const;
685
+ * constructor(message: string) {
686
+ * super({ message });
687
+ * }
688
+ * }
689
+ *
690
+ * const result = Result.fail(new InvalidValueError('Value must be positive'));
691
+ * // result.isFailure === true
692
+ * // result.getError() === InvalidValueError instance
693
+ * ```
694
+ *
695
+ * @example
696
+ * ```typescript
697
+ * // With custom error type
698
+ * interface ValidationError {
699
+ * field: string;
700
+ * message: string;
701
+ * }
702
+ *
703
+ * const result = Result.fail<never, ValidationError>({
704
+ * field: 'email',
705
+ * message: 'Invalid email format'
706
+ * });
707
+ * ```
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * // In a validation function
712
+ * function validateAge(age: number): Result<number, DomainError> {
713
+ * if (age < 0) {
714
+ * return Result.fail(new InvalidValueError('Age cannot be negative'));
715
+ * }
716
+ * if (age > 150) {
717
+ * return Result.fail(new InvalidValueError('Age seems unrealistic'));
718
+ * }
719
+ * return Result.ok(age);
720
+ * }
608
721
  * ```
609
722
  */
610
- static fail<T, E>(error: E): Result<T, E>;
723
+ static fail<T = never, E = DomainError>(error: E): Result<T, E>;
611
724
  /**
612
- * Creates a successful {@link Result}.
725
+ * Creates a successful result.
726
+ *
727
+ * @template T The type of the successful result.
728
+ * @template E The type of the error (never for success).
729
+ * @param value The success value.
730
+ * @returns {Result<T, E>} A successful Result instance.
731
+ *
732
+ * @example
733
+ * ```typescript
734
+ * // With primitive value
735
+ * const result = Result.ok(42);
736
+ * // result.isSuccess === true
737
+ * // result.getValue() === 42
738
+ * ```
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * // With object
743
+ * const user = { id: 1, name: 'John', email: 'john@example.com' };
744
+ * const result = Result.ok(user);
745
+ * if (result.isSuccess) {
746
+ * const savedUser = result.getValue(); // { id: 1, name: 'John', ... }
747
+ * }
748
+ * ```
749
+ *
750
+ * @example
751
+ * ```typescript
752
+ * // With domain entity
753
+ * class User {
754
+ * constructor(public readonly id: number, public readonly name: string) {}
755
+ * }
756
+ *
757
+ * function createUser(name: string): Result<User, DomainError> {
758
+ * const user = new User(Date.now(), name);
759
+ * return Result.ok(user);
760
+ * }
613
761
  *
614
- * @param value - Value representing a successful outcome
615
- * @returns A success {@link Result} containing the provided value
762
+ * const result = createUser('Alice');
763
+ * if (result.isSuccess) {
764
+ * console.log(`Created user: ${result.getValue()?.name}`);
765
+ * }
766
+ * ```
616
767
  *
617
768
  * @example
618
- * ```ts
619
- * return Result.ok(user);
769
+ * ```typescript
770
+ * // With void/undefined (for operations that don't return a value)
771
+ * function deleteUser(id: number): Result<void, DomainError> {
772
+ * // ... deletion logic ...
773
+ * return Result.ok(undefined);
774
+ * }
775
+ *
776
+ * const result = deleteUser(123);
777
+ * if (result.isSuccess) {
778
+ * console.log('User deleted successfully');
779
+ * }
620
780
  * ```
621
781
  */
622
- static ok<T, E>(value: T): Result<T, E>;
782
+ static ok<T, E = never>(value: T): Result<T, E>;
623
783
  /**
624
- * Returns the failure error.
784
+ * Returns the error if present, otherwise undefined.
625
785
  *
626
- * @returns The error associated with a failed result
786
+ * **Note:** Always check `isFailure` before calling this method to ensure
787
+ * the result is actually a failure. This method will return `undefined` for
788
+ * successful results.
627
789
  *
628
- * @throws {Error}
629
- * Thrown if this result represents a success.
630
- * This is a fail-fast guard against incorrect usage.
790
+ * @returns {E | undefined} The error or undefined if successful.
631
791
  *
632
792
  * @example
633
- * ```ts
793
+ * ```typescript
794
+ * const result = Result.fail(new InvalidValueError('Invalid input'));
795
+ *
634
796
  * if (result.isFailure) {
635
797
  * const error = result.getError();
798
+ * if (error) {
799
+ * console.error(`Error code: ${error.code}, Message: ${error.message}`);
800
+ * }
801
+ * }
802
+ * ```
803
+ *
804
+ * @example
805
+ * ```typescript
806
+ * // Safe error handling pattern
807
+ * function handleResult<T>(result: Result<T, DomainError>): void {
808
+ * if (result.isFailure) {
809
+ * const error = result.getError();
810
+ * if (error) {
811
+ * // Log error with metadata
812
+ * console.error({
813
+ * code: error.code,
814
+ * message: error.message,
815
+ * metadata: error.metadata
816
+ * });
817
+ * }
818
+ * }
636
819
  * }
637
820
  * ```
638
821
  */
639
- getError(): E;
822
+ getError(): E | undefined;
640
823
  /**
641
- * Returns the success value.
824
+ * Returns the value if present, otherwise undefined.
825
+ *
826
+ * **Note:** Always check `isSuccess` before calling this method to ensure
827
+ * the result is actually a success. This method will return `undefined` for
828
+ * failed results.
829
+ *
830
+ * @returns {T | undefined} The value or undefined if failed.
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * const result = Result.ok({ id: 1, name: 'John' });
642
835
  *
643
- * @returns The value associated with a successful result
836
+ * if (result.isSuccess) {
837
+ * const user = result.getValue();
838
+ * if (user) {
839
+ * console.log(`User: ${user.name} (ID: ${user.id})`);
840
+ * }
841
+ * }
842
+ * ```
644
843
  *
645
- * @throws {Error}
646
- * Thrown if this result represents a failure.
647
- * This is a fail-fast guard against incorrect usage.
844
+ * @example
845
+ * ```typescript
846
+ * // Type-safe value extraction
847
+ * function processUser(result: Result<User, DomainError>): void {
848
+ * if (result.isSuccess) {
849
+ * const user = result.getValue();
850
+ * // TypeScript knows user is User | undefined here
851
+ * if (user) {
852
+ * // Process the user
853
+ * userRepository.save(user);
854
+ * }
855
+ * }
856
+ * }
857
+ * ```
648
858
  *
649
859
  * @example
650
- * ```ts
860
+ * ```typescript
861
+ * // Using non-null assertion (use with caution)
862
+ * const result = Result.ok(42);
651
863
  * if (result.isSuccess) {
652
- * const value = result.getValue();
864
+ * const value = result.getValue()!; // Safe because we checked isSuccess
865
+ * console.log(value * 2); // 84
653
866
  * }
654
867
  * ```
655
868
  */
656
- getValue(): T;
869
+ getValue(): T | undefined;
870
+ /**
871
+ * Type guard for failure.
872
+ */
873
+ isFailureResult(): this is Result<never, E>;
874
+ /**
875
+ * Type guard for success.
876
+ */
877
+ isSuccessResult(): this is Result<T, never>;
657
878
  }
658
879
 
659
880
  /**
@@ -717,4 +938,4 @@ type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V
717
938
  declare function unwrapValueObject<T>(input: T, seen?: WeakSet<object>): UnwrapValueObject<T>;
718
939
  declare function ensureObject<T>(input: UnwrapValueObject<T>): object;
719
940
 
720
- export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, DomainViolation, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, Result, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
941
+ export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError$1 as DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, Result, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };