@woltz/rich-domain 0.2.2 → 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.
Files changed (148) hide show
  1. package/CHANGELOG.md +23 -75
  2. package/LICENSE +20 -20
  3. package/README.md +37 -20
  4. package/dist/base-entity.d.ts +2 -2
  5. package/dist/base-entity.d.ts.map +1 -1
  6. package/dist/base-entity.js +6 -4
  7. package/dist/base-entity.js.map +1 -1
  8. package/dist/criteria.d.ts +5 -11
  9. package/dist/criteria.d.ts.map +1 -1
  10. package/dist/criteria.js +4 -3
  11. package/dist/criteria.js.map +1 -1
  12. package/dist/deep-proxy.d.ts +3 -1
  13. package/dist/deep-proxy.d.ts.map +1 -1
  14. package/dist/deep-proxy.js +116 -29
  15. package/dist/deep-proxy.js.map +1 -1
  16. package/dist/domain-event-bus.d.ts +5 -6
  17. package/dist/domain-event-bus.d.ts.map +1 -1
  18. package/dist/domain-event-bus.js +3 -11
  19. package/dist/domain-event-bus.js.map +1 -1
  20. package/dist/domain-event.d.ts +1 -31
  21. package/dist/domain-event.d.ts.map +1 -1
  22. package/dist/domain-event.js +2 -1
  23. package/dist/domain-event.js.map +1 -1
  24. package/dist/entity.d.ts +2 -2
  25. package/dist/entity.js +1 -1
  26. package/dist/exceptions.d.ts +251 -0
  27. package/dist/exceptions.d.ts.map +1 -0
  28. package/dist/exceptions.js +321 -0
  29. package/dist/exceptions.js.map +1 -0
  30. package/dist/id.d.ts +3 -3
  31. package/dist/id.d.ts.map +1 -1
  32. package/dist/id.js +15 -4
  33. package/dist/id.js.map +1 -1
  34. package/dist/index.d.ts +2 -5
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -8
  37. package/dist/index.js.map +1 -1
  38. package/dist/paginated-result.d.ts.map +1 -1
  39. package/dist/paginated-result.js +12 -1
  40. package/dist/paginated-result.js.map +1 -1
  41. package/dist/repository/index.d.ts +2 -39
  42. package/dist/repository/index.d.ts.map +1 -1
  43. package/dist/repository/index.js +2 -39
  44. package/dist/repository/index.js.map +1 -1
  45. package/dist/repository/unit-of-work.d.ts +0 -11
  46. package/dist/repository/unit-of-work.d.ts.map +1 -1
  47. package/dist/repository/unit-of-work.js +0 -35
  48. package/dist/repository/unit-of-work.js.map +1 -1
  49. package/dist/types/criteria.d.ts +6 -2
  50. package/dist/types/criteria.d.ts.map +1 -1
  51. package/dist/types/criteria.js +1 -1
  52. package/dist/types/criteria.js.map +1 -1
  53. package/dist/types/domain-event.d.ts +32 -0
  54. package/dist/types/domain-event.d.ts.map +1 -0
  55. package/dist/types/domain-event.js +2 -0
  56. package/dist/types/domain-event.js.map +1 -0
  57. package/dist/types/domain.d.ts +2 -2
  58. package/dist/types/domain.d.ts.map +1 -1
  59. package/dist/types/history-tracker.d.ts +1 -1
  60. package/dist/types/history-tracker.d.ts.map +1 -1
  61. package/dist/types/index.d.ts +1 -0
  62. package/dist/types/index.d.ts.map +1 -1
  63. package/dist/types/index.js +1 -0
  64. package/dist/types/index.js.map +1 -1
  65. package/dist/value-object.d.ts +1 -1
  66. package/dist/value-object.d.ts.map +1 -1
  67. package/dist/value-object.js +2 -5
  68. package/dist/value-object.js.map +1 -1
  69. package/eslint.config.js +3 -3
  70. package/jest.config.js +1 -1
  71. package/package.json +14 -20
  72. package/src/base-entity.ts +6 -5
  73. package/src/criteria.ts +11 -11
  74. package/src/deep-proxy.ts +447 -339
  75. package/src/domain-event-bus.ts +152 -166
  76. package/src/domain-event.ts +53 -90
  77. package/src/entity.ts +16 -16
  78. package/src/exceptions.ts +435 -0
  79. package/src/id.ts +107 -94
  80. package/src/index.ts +26 -9
  81. package/src/paginated-result.ts +14 -1
  82. package/src/repository/index.ts +2 -44
  83. package/src/repository/unit-of-work.ts +1 -44
  84. package/src/types/criteria.ts +7 -2
  85. package/src/types/domain-event.ts +38 -0
  86. package/src/types/domain.ts +2 -3
  87. package/src/types/history-tracker.ts +1 -1
  88. package/src/types/index.ts +1 -0
  89. package/src/validation-error.ts +97 -97
  90. package/src/value-object.ts +3 -6
  91. package/tests/criteria.test.ts +8 -0
  92. package/tests/domain-events.test.ts +431 -445
  93. package/tests/entity-validation.test.ts +2 -2
  94. package/tests/entity.test.ts +33 -33
  95. package/tests/history-tracker.spec.ts +57 -17
  96. package/tests/id.test.ts +341 -341
  97. package/tests/repository.test.ts +8 -4
  98. package/tests/to-json.test.ts +103 -91
  99. package/tests/utils.ts +254 -151
  100. package/tests/value-object-validation.test.ts +0 -9
  101. package/tests/value-objects.test.ts +52 -52
  102. package/tsconfig.json +2 -24
  103. package/.github/workflows/ci.yml +0 -40
  104. package/.husky/commit-msg +0 -1
  105. package/.husky/pre-commit +0 -1
  106. package/.vscode/settings.json +0 -3
  107. package/commitlint.config.js +0 -23
  108. package/dist/filtering.d.ts +0 -107
  109. package/dist/filtering.d.ts.map +0 -1
  110. package/dist/filtering.js +0 -202
  111. package/dist/filtering.js.map +0 -1
  112. package/dist/ordering.d.ts +0 -93
  113. package/dist/ordering.d.ts.map +0 -1
  114. package/dist/ordering.js +0 -154
  115. package/dist/ordering.js.map +0 -1
  116. package/dist/pagination.d.ts +0 -218
  117. package/dist/pagination.d.ts.map +0 -1
  118. package/dist/pagination.js +0 -281
  119. package/dist/pagination.js.map +0 -1
  120. package/dist/repository/in-memory-repository.d.ts +0 -50
  121. package/dist/repository/in-memory-repository.d.ts.map +0 -1
  122. package/dist/repository/in-memory-repository.js +0 -93
  123. package/dist/repository/in-memory-repository.js.map +0 -1
  124. package/dist/repository/mapper.d.ts +0 -56
  125. package/dist/repository/mapper.d.ts.map +0 -1
  126. package/dist/repository/mapper.js +0 -15
  127. package/dist/repository/mapper.js.map +0 -1
  128. package/dist/repository/types.d.ts +0 -87
  129. package/dist/repository/types.d.ts.map +0 -1
  130. package/dist/repository/types.js +0 -6
  131. package/dist/repository/types.js.map +0 -1
  132. package/dist/repository.d.ts +0 -2
  133. package/dist/repository.d.ts.map +0 -1
  134. package/dist/repository.js +0 -21
  135. package/dist/repository.js.map +0 -1
  136. package/dist/specification.d.ts +0 -102
  137. package/dist/specification.d.ts.map +0 -1
  138. package/dist/specification.js +0 -187
  139. package/dist/specification.js.map +0 -1
  140. package/dist/types/repository.d.ts +0 -43
  141. package/dist/types/repository.d.ts.map +0 -1
  142. package/dist/types/repository.js +0 -2
  143. package/dist/types/repository.js.map +0 -1
  144. package/dist/types.d.ts +0 -88
  145. package/dist/types.d.ts.map +0 -1
  146. package/dist/types.js +0 -12
  147. package/dist/types.js.map +0 -1
  148. package/src/repository/in-memory-repository.ts +0 -116
@@ -1,166 +1,152 @@
1
- // ============================================================================
2
- // Domain Event Bus - Pub/Sub for Domain Events
3
- // ============================================================================
4
-
5
- import {
6
- IDomainEvent,
7
- DomainEventHandler,
8
- IDomainEventHandler,
9
- } from "./domain-event";
10
-
11
- type EventConstructor<T extends IDomainEvent = IDomainEvent> = new (
12
- ...args: any[]
13
- ) => T;
14
-
15
- /**
16
- * Domain Event Bus - Singleton pattern for event pub/sub
17
- */
18
- export class DomainEventBus {
19
- private static instance: DomainEventBus;
20
- private handlers: Map<
21
- string,
22
- Set<DomainEventHandler<any> | IDomainEventHandler<any>>
23
- > = new Map();
24
- private wildcardHandlers: Set<
25
- DomainEventHandler<any> | IDomainEventHandler<any>
26
- > = new Set();
27
-
28
- private constructor() {}
29
-
30
- /**
31
- * Get the singleton instance
32
- */
33
- static getInstance(): DomainEventBus {
34
- if (!DomainEventBus.instance) {
35
- DomainEventBus.instance = new DomainEventBus();
36
- }
37
- return DomainEventBus.instance;
38
- }
39
-
40
- /**
41
- * Subscribe to a specific event type
42
- */
43
- subscribe<T extends IDomainEvent>(
44
- eventType: EventConstructor<T> | string,
45
- handler: DomainEventHandler<T> | IDomainEventHandler<T>
46
- ): void {
47
- const eventName =
48
- typeof eventType === "string" ? eventType : eventType.name;
49
-
50
- if (!this.handlers.has(eventName)) {
51
- this.handlers.set(eventName, new Set());
52
- }
53
-
54
- this.handlers.get(eventName)!.add(handler);
55
- }
56
-
57
- /**
58
- * Subscribe to all events (wildcard)
59
- */
60
- subscribeAll(
61
- handler: DomainEventHandler<IDomainEvent> | IDomainEventHandler<IDomainEvent>
62
- ): void {
63
- this.wildcardHandlers.add(handler);
64
- }
65
-
66
- /**
67
- * Unsubscribe from a specific event type
68
- */
69
- unsubscribe<T extends IDomainEvent>(
70
- eventType: EventConstructor<T> | string,
71
- handler: DomainEventHandler<T> | IDomainEventHandler<T>
72
- ): void {
73
- const eventName =
74
- typeof eventType === "string" ? eventType : eventType.name;
75
- const handlers = this.handlers.get(eventName);
76
-
77
- if (handlers) {
78
- handlers.delete(handler);
79
- if (handlers.size === 0) {
80
- this.handlers.delete(eventName);
81
- }
82
- }
83
- }
84
-
85
- /**
86
- * Unsubscribe from all events
87
- */
88
- unsubscribeAll(
89
- handler: DomainEventHandler<IDomainEvent> | IDomainEventHandler<IDomainEvent>
90
- ): void {
91
- this.wildcardHandlers.delete(handler);
92
- }
93
-
94
- /**
95
- * Publish a single event
96
- */
97
- async publish<T extends IDomainEvent>(event: T): Promise<void> {
98
- const eventName = event.eventName;
99
- const handlers = this.handlers.get(eventName) || new Set();
100
-
101
- // Execute specific handlers
102
- const specificPromises = Array.from(handlers).map((handler) =>
103
- this.executeHandler(handler, event)
104
- );
105
-
106
- // Execute wildcard handlers
107
- const wildcardPromises = Array.from(this.wildcardHandlers).map((handler) =>
108
- this.executeHandler(handler, event)
109
- );
110
-
111
- await Promise.all([...specificPromises, ...wildcardPromises]);
112
- }
113
-
114
- /**
115
- * Publish multiple events
116
- */
117
- async publishAll(events: IDomainEvent[]): Promise<void> {
118
- await Promise.all(events.map((event) => this.publish(event)));
119
- }
120
-
121
- /**
122
- * Clear all handlers (useful for testing)
123
- */
124
- clearAllHandlers(): void {
125
- this.handlers.clear();
126
- this.wildcardHandlers.clear();
127
- }
128
-
129
- /**
130
- * Get count of handlers for an event type
131
- */
132
- getHandlerCount(eventType: EventConstructor | string): number {
133
- const eventName =
134
- typeof eventType === "string" ? eventType : eventType.name;
135
- return this.handlers.get(eventName)?.size || 0;
136
- }
137
-
138
- /**
139
- * Execute a handler (function or class)
140
- */
141
- private async executeHandler(
142
- handler: DomainEventHandler<any> | IDomainEventHandler<any>,
143
- event: IDomainEvent
144
- ): Promise<void> {
145
- try {
146
- if (typeof handler === "function") {
147
- await handler(event);
148
- } else {
149
- await handler.handle(event);
150
- }
151
- } catch (error) {
152
- console.error(
153
- `Error handling event ${event.eventName}:`,
154
- error
155
- );
156
- // Don't throw - we don't want one handler failure to break others
157
- }
158
- }
159
- }
160
-
161
- /**
162
- * Convenience function to get the event bus instance
163
- */
164
- export function getEventBus(): DomainEventBus {
165
- return DomainEventBus.getInstance();
166
- }
1
+ import { IDomainEvent, DomainEventHandler, IDomainEventHandler } from ".";
2
+
3
+ type EventConstructor<T extends IDomainEvent = IDomainEvent> = new (
4
+ ...args: any[]
5
+ ) => T;
6
+
7
+ /**
8
+ * Domain Event Bus - Singleton pattern for event pub/sub
9
+ */
10
+ export class DomainEventBus {
11
+ private static instance: DomainEventBus;
12
+ private handlers: Map<
13
+ string,
14
+ Set<DomainEventHandler<any> | IDomainEventHandler<any>>
15
+ > = new Map();
16
+ private wildcardHandlers: Set<
17
+ DomainEventHandler<any> | IDomainEventHandler<any>
18
+ > = new Set();
19
+
20
+ private constructor() {}
21
+
22
+ /**
23
+ * Get the singleton instance
24
+ */
25
+ static getInstance(): DomainEventBus {
26
+ if (!DomainEventBus.instance) {
27
+ DomainEventBus.instance = new DomainEventBus();
28
+ }
29
+ return DomainEventBus.instance;
30
+ }
31
+
32
+ /**
33
+ * Subscribe to a specific event type
34
+ */
35
+ subscribe<T extends IDomainEvent>(props: {
36
+ event: EventConstructor<T> | string;
37
+ handler: DomainEventHandler<T> | IDomainEventHandler<T>;
38
+ }): void {
39
+ const { event, handler } = props;
40
+ const eventName = typeof event === "string" ? event : event.name;
41
+
42
+ if (!this.handlers.has(eventName)) {
43
+ this.handlers.set(eventName, new Set());
44
+ }
45
+
46
+ this.handlers.get(eventName)!.add(handler);
47
+ }
48
+
49
+ /**
50
+ * Subscribe to all events (wildcard)
51
+ */
52
+ subscribeAll(
53
+ handler:
54
+ | DomainEventHandler<IDomainEvent>
55
+ | IDomainEventHandler<IDomainEvent>
56
+ ): void {
57
+ this.wildcardHandlers.add(handler);
58
+ }
59
+
60
+ /**
61
+ * Unsubscribe from a specific event type
62
+ */
63
+ unsubscribe<T extends IDomainEvent>(
64
+ eventType: EventConstructor<T> | string,
65
+ handler: DomainEventHandler<T> | IDomainEventHandler<T>
66
+ ): void {
67
+ const eventName =
68
+ typeof eventType === "string" ? eventType : eventType.name;
69
+ const handlers = this.handlers.get(eventName);
70
+
71
+ if (handlers) {
72
+ handlers.delete(handler);
73
+ if (handlers.size === 0) {
74
+ this.handlers.delete(eventName);
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Unsubscribe from all events
81
+ */
82
+ unsubscribeAll(
83
+ handler:
84
+ | DomainEventHandler<IDomainEvent>
85
+ | IDomainEventHandler<IDomainEvent>
86
+ ): void {
87
+ this.wildcardHandlers.delete(handler);
88
+ }
89
+
90
+ /**
91
+ * Publish a single event
92
+ */
93
+ async publish<T extends IDomainEvent>(event: T): Promise<void> {
94
+ const eventName = event.eventName;
95
+ const handlers = this.handlers.get(eventName) || new Set();
96
+
97
+ // Execute specific handlers
98
+ const specificPromises = Array.from(handlers).map((handler) =>
99
+ this.executeHandler(handler, event)
100
+ );
101
+
102
+ // Execute wildcard handlers
103
+ const wildcardPromises = Array.from(this.wildcardHandlers).map((handler) =>
104
+ this.executeHandler(handler, event)
105
+ );
106
+
107
+ await Promise.all([...specificPromises, ...wildcardPromises]);
108
+ }
109
+
110
+ /**
111
+ * Publish multiple events
112
+ */
113
+ async publishAll(events: IDomainEvent[]): Promise<void> {
114
+ await Promise.all(events.map((event) => this.publish(event)));
115
+ }
116
+
117
+ /**
118
+ * Clear all handlers (useful for testing)
119
+ */
120
+ clearAllHandlers(): void {
121
+ this.handlers.clear();
122
+ this.wildcardHandlers.clear();
123
+ }
124
+
125
+ /**
126
+ * Get count of handlers for an event type
127
+ */
128
+ getHandlerCount(eventType: EventConstructor | string): number {
129
+ const eventName =
130
+ typeof eventType === "string" ? eventType : eventType.name;
131
+ return this.handlers.get(eventName)?.size || 0;
132
+ }
133
+
134
+ /**
135
+ * Execute a handler (function or class)
136
+ */
137
+ private async executeHandler(
138
+ handler: DomainEventHandler<any> | IDomainEventHandler<any>,
139
+ event: IDomainEvent
140
+ ): Promise<void> {
141
+ try {
142
+ if (typeof handler === "function") {
143
+ await handler(event);
144
+ } else {
145
+ await handler.handle(event);
146
+ }
147
+ } catch (error) {
148
+ console.error(`Error handling event ${event.eventName}:`, error);
149
+ // Don't throw - we don't want one handler failure to break others
150
+ }
151
+ }
152
+ }
@@ -1,90 +1,53 @@
1
- // ============================================================================
2
- // Domain Events - Event-Driven Architecture Support
3
- // ============================================================================
4
-
5
- import { Id } from "./id";
6
-
7
- /**
8
- * Interface for all domain events
9
- */
10
- export interface IDomainEvent {
11
- /**
12
- * Unique identifier for this event occurrence
13
- */
14
- readonly eventId: string;
15
-
16
- /**
17
- * When the event occurred
18
- */
19
- readonly occurredOn: Date;
20
-
21
- /**
22
- * Name/type of the event (e.g., "UserCreated", "OrderPlaced")
23
- */
24
- readonly eventName: string;
25
-
26
- /**
27
- * ID of the aggregate that raised this event
28
- */
29
- readonly aggregateId: string;
30
- }
31
-
32
- /**
33
- * Base class for domain events
34
- */
35
- export abstract class DomainEvent implements IDomainEvent {
36
- public readonly eventId: string;
37
- public readonly occurredOn: Date;
38
- public readonly aggregateId: string;
39
-
40
- constructor(aggregateId: Id | string) {
41
- this.eventId = this.generateEventId();
42
- this.occurredOn = new Date();
43
- this.aggregateId = aggregateId instanceof Id ? aggregateId.value : aggregateId;
44
- }
45
-
46
- /**
47
- * Get the event name (defaults to class name)
48
- */
49
- get eventName(): string {
50
- return this.constructor.name;
51
- }
52
-
53
- private generateEventId(): string {
54
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
55
- }
56
-
57
- /**
58
- * Convert event to JSON
59
- */
60
- toJSON(): Record<string, any> {
61
- return {
62
- eventId: this.eventId,
63
- eventName: this.eventName,
64
- occurredOn: this.occurredOn.toISOString(),
65
- aggregateId: this.aggregateId,
66
- ...this.getPayload(),
67
- };
68
- }
69
-
70
- /**
71
- * Override this to provide event-specific data
72
- */
73
- protected getPayload(): Record<string, any> {
74
- return {};
75
- }
76
- }
77
-
78
- /**
79
- * Event handler function type
80
- */
81
- export type DomainEventHandler<T extends IDomainEvent = IDomainEvent> = (
82
- event: T
83
- ) => void | Promise<void>;
84
-
85
- /**
86
- * Event handler class type
87
- */
88
- export interface IDomainEventHandler<T extends IDomainEvent = IDomainEvent> {
89
- handle(event: T): void | Promise<void>;
90
- }
1
+ // ============================================================================
2
+ // Domain Events - Event-Driven Architecture Support
3
+ // ============================================================================
4
+
5
+ import { IDomainEvent } from ".";
6
+ import { Id } from "./id";
7
+
8
+ /**
9
+ * Base class for domain events
10
+ */
11
+ export abstract class DomainEvent implements IDomainEvent {
12
+ public readonly eventId: string;
13
+ public readonly occurredOn: Date;
14
+ public readonly aggregateId: string;
15
+
16
+ constructor(aggregateId: Id | string) {
17
+ this.eventId = this.generateEventId();
18
+ this.occurredOn = new Date();
19
+ this.aggregateId =
20
+ aggregateId instanceof Id ? aggregateId.value : aggregateId;
21
+ }
22
+
23
+ /**
24
+ * Get the event name (defaults to class name)
25
+ */
26
+ get eventName(): string {
27
+ return this.constructor.name;
28
+ }
29
+
30
+ private generateEventId(): string {
31
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
32
+ }
33
+
34
+ /**
35
+ * Convert event to JSON
36
+ */
37
+ toJSON(): Record<string, any> {
38
+ return {
39
+ eventId: this.eventId,
40
+ eventName: this.eventName,
41
+ occurredOn: this.occurredOn.toISOString(),
42
+ aggregateId: this.aggregateId,
43
+ ...this.getPayload(),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Override this to provide event-specific data
49
+ */
50
+ protected getPayload(): Record<string, any> {
51
+ return {};
52
+ }
53
+ }
package/src/entity.ts CHANGED
@@ -1,16 +1,16 @@
1
- // ============================================================================
2
- // Entity & Aggregate Classes
3
- // ============================================================================
4
-
5
- import { BaseEntity } from './base-entity';
6
- import { BaseProps } from './types';
7
-
8
- /**
9
- * Entity - Has identity and lifecycle, but is not an aggregate root
10
- */
11
- export class Entity<T extends BaseProps> extends BaseEntity<T> {}
12
-
13
- /**
14
- * Aggregate - Aggregate root that manages a consistency boundary
15
- */
16
- export class Aggregate<T extends BaseProps> extends BaseEntity<T> {}
1
+ // ============================================================================
2
+ // Entity & Aggregate Classes
3
+ // ============================================================================
4
+
5
+ import { BaseEntity } from "./base-entity";
6
+ import { BaseProps } from "./types";
7
+
8
+ /**
9
+ * Entity - Has identity and lifecycle, but is not an aggregate root
10
+ */
11
+ export class Entity<T extends BaseProps> extends BaseEntity<T> {}
12
+
13
+ /**
14
+ * Aggregate - Aggregate root that manages a consistency boundary
15
+ */
16
+ export class Aggregate<T extends BaseProps> extends BaseEntity<T> {}