@rineex/ddd 0.1.0 → 1.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 +262 -62
- package/dist/index.d.ts +262 -62
- package/dist/index.js +1 -446
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -403
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
package/dist/index.d.mts
CHANGED
|
@@ -27,47 +27,6 @@ interface ApplicationServicePort<I, O> {
|
|
|
27
27
|
execute: (args: I) => Promise<O>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
interface DomainErrorMetadata {
|
|
31
|
-
cause?: {
|
|
32
|
-
name: string;
|
|
33
|
-
message: string;
|
|
34
|
-
stack?: string;
|
|
35
|
-
};
|
|
36
|
-
[key: string]: unknown;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Base class for all Domain Errors in the application.
|
|
40
|
-
*
|
|
41
|
-
* This class ensures:
|
|
42
|
-
* 1. Serializable and structured for logs or API responses.
|
|
43
|
-
* 2. Identifiable via stable error codes (not just class names).
|
|
44
|
-
* 3. Contextual with optional structured metadata.
|
|
45
|
-
* 4. Supports error chaining (cause) and stack trace preservation.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* export class InsufficientFundsError extends DomainError {
|
|
49
|
-
* constructor(accountId: string, currentBalance: number) {
|
|
50
|
-
* super(
|
|
51
|
-
* `Account ${accountId} has insufficient funds.`,
|
|
52
|
-
* 'INSUFFICIENT_FUNDS',
|
|
53
|
-
* { accountId, currentBalance }
|
|
54
|
-
* );
|
|
55
|
-
* }
|
|
56
|
-
* }
|
|
57
|
-
*/
|
|
58
|
-
declare abstract class DomainError extends Error {
|
|
59
|
-
/** Stable, machine-readable error code */
|
|
60
|
-
readonly code: string;
|
|
61
|
-
/** Structured, immutable domain metadata */
|
|
62
|
-
readonly metadata: Readonly<DomainErrorMetadata>;
|
|
63
|
-
/**
|
|
64
|
-
* @param message - Human-readable error message
|
|
65
|
-
* @param code - Stable error code
|
|
66
|
-
* @param metadata - Domain-specific structured data; optional `cause` can be included
|
|
67
|
-
*/
|
|
68
|
-
protected constructor(message: string, code: string, metadata?: DomainErrorMetadata);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
30
|
declare abstract class ValueObject<T> {
|
|
72
31
|
get value(): T;
|
|
73
32
|
protected readonly props: Readonly<T>;
|
|
@@ -91,17 +50,6 @@ declare abstract class ValueObject<T> {
|
|
|
91
50
|
protected abstract validate(props: T): void;
|
|
92
51
|
}
|
|
93
52
|
|
|
94
|
-
/**
|
|
95
|
-
* Custom error class for entity validation failures.
|
|
96
|
-
*/
|
|
97
|
-
declare class EntityValidationError extends DomainError {
|
|
98
|
-
constructor(message: string, cause?: Error);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
declare class InvalidValueObjectError extends DomainError {
|
|
102
|
-
constructor(message: string);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
53
|
/**
|
|
106
54
|
* AggregateId is a ValueObject that represents a unique identifier for an aggregate.
|
|
107
55
|
*/
|
|
@@ -143,6 +91,15 @@ declare class AggregateId extends ValueObject<string> {
|
|
|
143
91
|
protected validate(value: string): void;
|
|
144
92
|
}
|
|
145
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
96
|
+
*
|
|
97
|
+
* @param obj - The object to be deeply frozen.
|
|
98
|
+
* @param seen - A WeakSet to track already processed objects (for circular references).
|
|
99
|
+
* @returns A deeply frozen version of the input object.
|
|
100
|
+
*/
|
|
101
|
+
declare function deepFreeze<T>(obj: T, seen?: WeakSet<object>): Readonly<T>;
|
|
102
|
+
|
|
146
103
|
/**
|
|
147
104
|
* HTTP status code catalog.
|
|
148
105
|
*
|
|
@@ -153,6 +110,17 @@ declare class AggregateId extends ValueObject<string> {
|
|
|
153
110
|
* - Values are numeric status codes.
|
|
154
111
|
* - Frozen to prevent runtime mutation.
|
|
155
112
|
* - Exporting `HttpStatus` ensures type-safe usage across the codebase.
|
|
113
|
+
* Usage:
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { HttpStatus } from 'path-to-this-file';
|
|
116
|
+
*
|
|
117
|
+
* function handleRequest() {
|
|
118
|
+
* return {
|
|
119
|
+
* statusCode: HttpStatus.OK,
|
|
120
|
+
* body: 'Success',
|
|
121
|
+
* };
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
156
124
|
*/
|
|
157
125
|
declare const HttpStatus: Readonly<{
|
|
158
126
|
readonly REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
|
|
@@ -291,15 +259,6 @@ declare const HttpStatusMessage: {
|
|
|
291
259
|
};
|
|
292
260
|
type HttpStatusMessage = (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];
|
|
293
261
|
|
|
294
|
-
/**
|
|
295
|
-
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
296
|
-
*
|
|
297
|
-
* @param obj - The object to be deeply frozen.
|
|
298
|
-
* @param seen - A WeakSet to track already processed objects (for circular references).
|
|
299
|
-
* @returns A deeply frozen version of the input object
|
|
300
|
-
*/
|
|
301
|
-
declare function deepFreeze<T>(obj: T, seen?: WeakSet<object>): Readonly<T>;
|
|
302
|
-
|
|
303
262
|
interface ErrorParams {
|
|
304
263
|
message: string;
|
|
305
264
|
code: HttpStatusMessage;
|
|
@@ -352,4 +311,245 @@ type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V
|
|
|
352
311
|
declare function unwrapValueObject<T>(input: T, seen?: WeakSet<object>): UnwrapValueObject<T>;
|
|
353
312
|
declare function ensureObject<T>(input: UnwrapValueObject<T>): object;
|
|
354
313
|
|
|
355
|
-
|
|
314
|
+
interface EntityBaseInterface {
|
|
315
|
+
id: AggregateId;
|
|
316
|
+
equals: (entity: unknown) => boolean;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Base properties common to all entities, including ID and timestamps.
|
|
320
|
+
*/
|
|
321
|
+
interface BaseEntityProps {
|
|
322
|
+
/** Unique identifier for the entity */
|
|
323
|
+
id?: AggregateId;
|
|
324
|
+
/** Date when the entity was created */
|
|
325
|
+
createdAt: Date;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Interface for constructing an entity with optional timestamps.
|
|
329
|
+
* @template Props - The specific props type for the entity
|
|
330
|
+
*/
|
|
331
|
+
type CreateEntityProps<T> = BaseEntityProps & {
|
|
332
|
+
props: T;
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Abstract base class for domain entities in a Domain-Driven Design (DDD) context.
|
|
336
|
+
* Provides common functionality for entity identification, equality comparison,
|
|
337
|
+
* immutability, and validation. Entities extending this class must implement
|
|
338
|
+
* the `validate` method to enforce domain invariants.
|
|
339
|
+
* @template EntityProps - The specific props type for the entity
|
|
340
|
+
*/
|
|
341
|
+
declare abstract class Entity<EntityProps> {
|
|
342
|
+
#private;
|
|
343
|
+
/**
|
|
344
|
+
* Returns the creation timestamp.
|
|
345
|
+
* A new Date instance is returned to preserve immutability.
|
|
346
|
+
*
|
|
347
|
+
* @returns {Date} The creation date of the entity.
|
|
348
|
+
*/
|
|
349
|
+
get createdAt(): Date;
|
|
350
|
+
/**
|
|
351
|
+
* Gets the entity's unique identifier.
|
|
352
|
+
* @returns The entity's ID
|
|
353
|
+
*/
|
|
354
|
+
get id(): AggregateId;
|
|
355
|
+
get metadata(): Readonly<{
|
|
356
|
+
createdAt: string;
|
|
357
|
+
id: string;
|
|
358
|
+
}>;
|
|
359
|
+
/**
|
|
360
|
+
* Returns an immutable shallow copy of the entity's properties.
|
|
361
|
+
*
|
|
362
|
+
* @returns {Readonly<EntityProps>} The entity domain properties.
|
|
363
|
+
*/
|
|
364
|
+
get props(): Readonly<EntityProps>;
|
|
365
|
+
/**
|
|
366
|
+
* Constructs an entity with the provided properties and timestamps.
|
|
367
|
+
* Ensures immutability by cloning props and validates the entity.
|
|
368
|
+
* @param params - Entity creation parameters
|
|
369
|
+
* @throws EntityValidationError if the ID is empty or validation fails
|
|
370
|
+
*/
|
|
371
|
+
protected constructor({ createdAt, props, id, }: CreateEntityProps<EntityProps>);
|
|
372
|
+
/**
|
|
373
|
+
* Checks if the provided value is an instance of Entity.
|
|
374
|
+
* @param entity - The value to check
|
|
375
|
+
* @returns True if the value is an Entity instance
|
|
376
|
+
*/
|
|
377
|
+
static isEntity(entity: unknown): entity is EntityBaseInterface;
|
|
378
|
+
/**
|
|
379
|
+
* Compares this entity with another to determine if they are the same.
|
|
380
|
+
* Equality is based on the entity ID.
|
|
381
|
+
* @param other - The entity to compare with
|
|
382
|
+
* @returns True if the entities have the same ID
|
|
383
|
+
*/
|
|
384
|
+
equals(other?: Entity<EntityProps>): boolean;
|
|
385
|
+
/**
|
|
386
|
+
* Returns a frozen copy of the entity's properties, including base properties.
|
|
387
|
+
* Ensures immutability by returning a new object.
|
|
388
|
+
* @returns A frozen copy of the entity's properties
|
|
389
|
+
*/
|
|
390
|
+
getPropsCopy(): Readonly<EntityProps>;
|
|
391
|
+
/**
|
|
392
|
+
* Determines if the entity is transient, i.e., it has not been persisted yet.
|
|
393
|
+
* By convention, an entity is considered transient if it lacks a valid identifier.
|
|
394
|
+
* This can be useful when performing logic that depends on persistence state,
|
|
395
|
+
* such as conditional inserts or validations that only apply to new entities.
|
|
396
|
+
*
|
|
397
|
+
* @returns True if the entity is transient (not persisted), otherwise false.
|
|
398
|
+
*/
|
|
399
|
+
isPersisted(): boolean;
|
|
400
|
+
toJSON(): Record<string, unknown>;
|
|
401
|
+
toObject(): Readonly<UnwrapValueObject<EntityProps> & {
|
|
402
|
+
createdAt: string;
|
|
403
|
+
id: string;
|
|
404
|
+
}>;
|
|
405
|
+
/**
|
|
406
|
+
* Validates the entity's state to enforce domain invariants.
|
|
407
|
+
* Must be implemented by subclasses to define specific validation rules.
|
|
408
|
+
* @throws EntityValidationError if validation fails
|
|
409
|
+
*/
|
|
410
|
+
abstract validate(): void;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
type Primitive = boolean | number | string | null;
|
|
414
|
+
type Serializable = Primitive | Serializable[] | {
|
|
415
|
+
[key: string]: Serializable;
|
|
416
|
+
};
|
|
417
|
+
type DomainEventPayload = Record<string, Serializable>;
|
|
418
|
+
type DomainEventProps<T> = {
|
|
419
|
+
id: string;
|
|
420
|
+
aggregateId: string;
|
|
421
|
+
schemaVersion: number;
|
|
422
|
+
occurredAt: number;
|
|
423
|
+
payload: T;
|
|
424
|
+
};
|
|
425
|
+
declare abstract class DomainEvent<T extends DomainEventPayload = DomainEventPayload> {
|
|
426
|
+
abstract readonly eventName: string;
|
|
427
|
+
readonly id: string;
|
|
428
|
+
readonly aggregateId: string;
|
|
429
|
+
readonly schemaVersion: number;
|
|
430
|
+
readonly occurredAt: number;
|
|
431
|
+
readonly payload: Readonly<T>;
|
|
432
|
+
protected constructor(props: DomainEventProps<T>);
|
|
433
|
+
toPrimitives(): {
|
|
434
|
+
schemaVersion: number;
|
|
435
|
+
aggregateId: string;
|
|
436
|
+
occurredAt: number;
|
|
437
|
+
eventName: string;
|
|
438
|
+
payload: Readonly<T>;
|
|
439
|
+
id: string;
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Interface for AggregateRoot to ensure type safety and extensibility.
|
|
445
|
+
*/
|
|
446
|
+
interface AggregateRootInterface {
|
|
447
|
+
readonly id: AggregateId;
|
|
448
|
+
readonly domainEvents: readonly DomainEvent[];
|
|
449
|
+
/**
|
|
450
|
+
* Validates the aggregate's invariants.
|
|
451
|
+
* @throws {EntityValidationError} If validation fails.
|
|
452
|
+
*/
|
|
453
|
+
validate: () => void;
|
|
454
|
+
/**
|
|
455
|
+
* Adds a domain event to the aggregate.
|
|
456
|
+
* @param event The domain event to add.
|
|
457
|
+
*/
|
|
458
|
+
addEvent: (event: DomainEvent) => void;
|
|
459
|
+
/**
|
|
460
|
+
* Retrieves and clears all domain events recorded by this aggregate.
|
|
461
|
+
*
|
|
462
|
+
* Domain events represent facts that occurred as a result of state changes
|
|
463
|
+
* within the aggregate. This method transfers ownership of those events
|
|
464
|
+
* to the application layer for further processing (e.g. publishing).
|
|
465
|
+
*
|
|
466
|
+
* Calling this method has the side effect of clearing the aggregate's
|
|
467
|
+
* internal event collection to prevent duplicate handling.
|
|
468
|
+
*
|
|
469
|
+
* This method is intended to be invoked by application services
|
|
470
|
+
* after the aggregate has been successfully persisted.
|
|
471
|
+
*
|
|
472
|
+
* @returns A read-only list of domain events raised by this aggregate.
|
|
473
|
+
*/
|
|
474
|
+
pullDomainEvents: () => readonly DomainEvent[];
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Base class for aggregate roots in DDD, encapsulating domain events and validation.
|
|
478
|
+
* @template EntityProps The type of the entity's properties.
|
|
479
|
+
*/
|
|
480
|
+
declare abstract class AggregateRoot<EntityProps> extends Entity<EntityProps> implements AggregateRootInterface {
|
|
481
|
+
/**
|
|
482
|
+
* Gets a read-only copy of the domain events.
|
|
483
|
+
*/
|
|
484
|
+
get domainEvents(): readonly DomainEvent[];
|
|
485
|
+
/**
|
|
486
|
+
* Internal list of domain events.
|
|
487
|
+
*/
|
|
488
|
+
private readonly _domainEvents;
|
|
489
|
+
/**
|
|
490
|
+
* Adds a domain event to the aggregate after validating invariants.
|
|
491
|
+
* @param domainEvent The domain event to add.
|
|
492
|
+
* @throws {EntityValidationError} If invariants are not met.
|
|
493
|
+
*/
|
|
494
|
+
addEvent(domainEvent: DomainEvent): void;
|
|
495
|
+
pullDomainEvents(): readonly DomainEvent[];
|
|
496
|
+
/**
|
|
497
|
+
* Validates the entity's invariants.
|
|
498
|
+
* @throws {EntityValidationError} If validation fails.
|
|
499
|
+
*/
|
|
500
|
+
abstract validate(): void;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
interface DomainErrorMetadata {
|
|
504
|
+
cause?: {
|
|
505
|
+
name: string;
|
|
506
|
+
message: string;
|
|
507
|
+
stack?: string;
|
|
508
|
+
};
|
|
509
|
+
[key: string]: unknown;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Base class for all Domain Errors in the application.
|
|
513
|
+
*
|
|
514
|
+
* This class ensures:
|
|
515
|
+
* 1. Serializable and structured for logs or API responses.
|
|
516
|
+
* 2. Identifiable via stable error codes (not just class names).
|
|
517
|
+
* 3. Contextual with optional structured metadata.
|
|
518
|
+
* 4. Supports error chaining (cause) and stack trace preservation.
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* export class InsufficientFundsError extends DomainError {
|
|
522
|
+
* constructor(accountId: string, currentBalance: number) {
|
|
523
|
+
* super(
|
|
524
|
+
* `Account ${accountId} has insufficient funds.`,
|
|
525
|
+
* 'INSUFFICIENT_FUNDS',
|
|
526
|
+
* { accountId, currentBalance }
|
|
527
|
+
* );
|
|
528
|
+
* }
|
|
529
|
+
* }
|
|
530
|
+
*/
|
|
531
|
+
declare abstract class DomainError extends Error {
|
|
532
|
+
/** Stable, machine-readable error code */
|
|
533
|
+
readonly code: string;
|
|
534
|
+
/** Structured, immutable domain metadata */
|
|
535
|
+
readonly metadata: Readonly<DomainErrorMetadata>;
|
|
536
|
+
/**
|
|
537
|
+
* @param message - Human-readable error message
|
|
538
|
+
* @param code - Stable error code
|
|
539
|
+
* @param metadata - Domain-specific structured data; optional `cause` can be included
|
|
540
|
+
*/
|
|
541
|
+
protected constructor(message: string, code: string, metadata?: DomainErrorMetadata);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Custom error class for entity validation failures.
|
|
546
|
+
*/
|
|
547
|
+
declare class EntityValidationError extends DomainError {
|
|
548
|
+
constructor(message: string, cause?: Error);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
declare class InvalidValueObjectError extends DomainError {
|
|
552
|
+
constructor(message: string);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export { AggregateId, AggregateRoot, type AggregateRootInterface, ApplicationError, type ApplicationServicePort, type BaseEntityProps, type CreateEntityProps, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityBaseInterface, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|