@shirudo/ddd-kit 0.10.0 → 0.12.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/README.md +188 -31
- package/dist/index.d.ts +539 -423
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -8,256 +8,242 @@ interface IdGenerator {
|
|
|
8
8
|
next: <T extends string>() => Id<T>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Functional definition of an Entity via its capability.
|
|
13
|
+
* An object is identifiable if it has an id.
|
|
14
|
+
*/
|
|
15
|
+
type Identifiable<TId> = {
|
|
16
|
+
readonly id: TId;
|
|
13
17
|
};
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
19
|
+
* Interface for Entities.
|
|
20
|
+
* Extends Identifiable and adds equality comparison behavior.
|
|
21
|
+
*
|
|
22
|
+
* @template TId - The type of the entity identifier
|
|
17
23
|
*/
|
|
18
|
-
interface
|
|
19
|
-
/**
|
|
20
|
-
* Correlation ID for tracing events across multiple services/components.
|
|
21
|
-
* Typically used to group related events in a distributed system.
|
|
22
|
-
*/
|
|
23
|
-
correlationId?: string;
|
|
24
|
-
/**
|
|
25
|
-
* Causation ID referencing the event or command that caused this event.
|
|
26
|
-
* Used to build event chains and understand causality.
|
|
27
|
-
*/
|
|
28
|
-
causationId?: string;
|
|
29
|
-
/**
|
|
30
|
-
* User ID of the person or system that triggered the event.
|
|
31
|
-
*/
|
|
32
|
-
userId?: string;
|
|
24
|
+
interface IEntity<TId> extends Identifiable<TId> {
|
|
33
25
|
/**
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
* Allows extensibility for domain-specific metadata.
|
|
26
|
+
* Checks if this entity is equal to another.
|
|
27
|
+
* Two entities are equal if they have the same ID.
|
|
28
|
+
*
|
|
29
|
+
* @param other - The other entity to compare
|
|
30
|
+
* @returns true if IDs are equal
|
|
40
31
|
*/
|
|
41
|
-
|
|
32
|
+
equals(other: Identifiable<TId>): boolean;
|
|
42
33
|
}
|
|
43
34
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
35
|
+
* Abstract base class for Entities.
|
|
36
|
+
* Provides identity equality and common behavior.
|
|
46
37
|
*
|
|
47
|
-
* @template
|
|
48
|
-
* @template P - The event payload type
|
|
38
|
+
* @template TId - The type of the entity identifier
|
|
49
39
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
type: T;
|
|
40
|
+
declare abstract class Entity<TId> implements IEntity<TId> {
|
|
41
|
+
readonly id: TId;
|
|
42
|
+
constructor(id: TId);
|
|
55
43
|
/**
|
|
56
|
-
*
|
|
44
|
+
* Optional validation hook that can be overridden by subclasses.
|
|
45
|
+
* Should throw an error if validation fails.
|
|
57
46
|
*/
|
|
58
|
-
|
|
47
|
+
protected validate(): void;
|
|
59
48
|
/**
|
|
60
|
-
*
|
|
49
|
+
* Serializes the entity to a plain object.
|
|
50
|
+
* Default implementation returns the entity as is (since it's an object with properties).
|
|
51
|
+
* Subclasses can override this to control JSON serialization.
|
|
61
52
|
*/
|
|
62
|
-
|
|
53
|
+
toJSON(): unknown;
|
|
63
54
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* Optional metadata for traceability, correlation, and auditing.
|
|
70
|
-
* Includes correlationId, causationId, userId, source, and custom fields.
|
|
55
|
+
* Checks if this entity is equal to another.
|
|
56
|
+
* Two entities are equal if they have the same ID.
|
|
57
|
+
*
|
|
58
|
+
* @param other - The other entity to compare
|
|
59
|
+
* @returns true if IDs are equal
|
|
71
60
|
*/
|
|
72
|
-
|
|
61
|
+
equals(other: Identifiable<TId>): boolean;
|
|
73
62
|
}
|
|
74
63
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* In Domain-Driven Design, an Aggregate Root is an Entity (the parent Entity of the aggregate).
|
|
78
|
-
* It represents the aggregate externally and is the only object that external code
|
|
79
|
-
* is allowed to hold references to. All access to child entities within the aggregate
|
|
80
|
-
* must go through the Aggregate Root.
|
|
81
|
-
*
|
|
82
|
-
* An Aggregate consists of:
|
|
83
|
-
* - One Aggregate Root (Entity with id + version)
|
|
84
|
-
* - Optional child entities (Entities with id, but no own version)
|
|
85
|
-
* - Optional value objects
|
|
86
|
-
*
|
|
87
|
-
* The Aggregate Root has identity (id) and version for optimistic concurrency control.
|
|
88
|
-
* Child entities exist only within the aggregate boundary and are versioned through
|
|
89
|
-
* the Aggregate Root.
|
|
64
|
+
* Checks if two entities have the same ID.
|
|
65
|
+
* Works with any object that has an 'id' property.
|
|
90
66
|
*
|
|
91
|
-
* @
|
|
67
|
+
* @param a - First entity
|
|
68
|
+
* @param b - Second entity
|
|
69
|
+
* @returns true if both entities have the same ID, false otherwise
|
|
92
70
|
*
|
|
93
71
|
* @example
|
|
94
72
|
* ```typescript
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* // OrderState contains child entities (e.g., OrderItem) and value objects
|
|
98
|
-
* }
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
interface AggregateRoot<TId extends Id<string>> {
|
|
102
|
-
/**
|
|
103
|
-
* Unique identifier of the aggregate root entity.
|
|
104
|
-
*/
|
|
105
|
-
readonly id: TId;
|
|
106
|
-
/**
|
|
107
|
-
* Version number for optimistic concurrency control.
|
|
108
|
-
* Incremented on each state change to detect concurrent modifications.
|
|
109
|
-
* This version applies to the entire aggregate, including all child entities.
|
|
110
|
-
*/
|
|
111
|
-
readonly version: Version;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Structural interface representing an aggregate with state and events.
|
|
115
|
-
* Used for type constraints in repositories and other infrastructure code.
|
|
73
|
+
* const item1: OrderItem = { id: itemId1, productId: "prod-1", quantity: 2 };
|
|
74
|
+
* const item2: OrderItem = { id: itemId2, productId: "prod-2", quantity: 1 };
|
|
116
75
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
76
|
+
* sameEntity(item1, item2); // false
|
|
77
|
+
* sameEntity(item1, item1); // true
|
|
78
|
+
* ```
|
|
119
79
|
*/
|
|
120
|
-
|
|
121
|
-
state: Readonly<State>;
|
|
122
|
-
version: Version;
|
|
123
|
-
pendingEvents: ReadonlyArray<Evt>;
|
|
124
|
-
}
|
|
125
|
-
declare function aggregate<State, Evt extends DomainEvent<string, unknown>>(state: State, version?: Version): Aggregate<State, Evt>;
|
|
126
|
-
declare function withEvent<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>, evt: E): Aggregate<S, E>;
|
|
127
|
-
declare function bump<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>): Aggregate<S, E>;
|
|
80
|
+
declare function sameEntity<TId>(a: Identifiable<TId>, b: Identifiable<TId>): boolean;
|
|
128
81
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
82
|
+
* Finds an entity by ID in a collection.
|
|
83
|
+
* Returns undefined if not found.
|
|
131
84
|
*
|
|
132
|
-
* @param
|
|
133
|
-
* @param
|
|
134
|
-
* @
|
|
135
|
-
* @returns A domain event
|
|
85
|
+
* @param entities - Array of entities to search
|
|
86
|
+
* @param id - The ID to search for
|
|
87
|
+
* @returns The entity if found, undefined otherwise
|
|
136
88
|
*
|
|
137
89
|
* @example
|
|
138
90
|
* ```typescript
|
|
139
|
-
* const
|
|
91
|
+
* const items: OrderItem[] = [
|
|
92
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
93
|
+
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
94
|
+
* ];
|
|
95
|
+
*
|
|
96
|
+
* const item = findEntityById(items, itemId1);
|
|
97
|
+
* // item is { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
140
98
|
* ```
|
|
141
99
|
*/
|
|
142
|
-
declare function
|
|
143
|
-
occurredAt?: Date;
|
|
144
|
-
version?: number;
|
|
145
|
-
metadata?: EventMetadata;
|
|
146
|
-
}): DomainEvent<T, P>;
|
|
100
|
+
declare function findEntityById<TId, T extends Identifiable<TId>>(entities: T[], id: TId): T | undefined;
|
|
147
101
|
/**
|
|
148
|
-
*
|
|
149
|
-
* Convenience function for creating events with correlation and causation IDs.
|
|
102
|
+
* Checks if an entity with the given ID exists in the collection.
|
|
150
103
|
*
|
|
151
|
-
* @param
|
|
152
|
-
* @param
|
|
153
|
-
* @
|
|
154
|
-
* @param options - Optional event configuration
|
|
155
|
-
* @returns A domain event with metadata
|
|
104
|
+
* @param entities - Array of entities to search
|
|
105
|
+
* @param id - The ID to check for
|
|
106
|
+
* @returns true if an entity with the ID exists, false otherwise
|
|
156
107
|
*
|
|
157
108
|
* @example
|
|
158
109
|
* ```typescript
|
|
159
|
-
* const
|
|
160
|
-
* "
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* userId: "user-789"
|
|
166
|
-
* }
|
|
167
|
-
* );
|
|
110
|
+
* const items: OrderItem[] = [
|
|
111
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
112
|
+
* ];
|
|
113
|
+
*
|
|
114
|
+
* hasEntityId(items, itemId1); // true
|
|
115
|
+
* hasEntityId(items, itemId2); // false
|
|
168
116
|
* ```
|
|
169
117
|
*/
|
|
170
|
-
declare function
|
|
171
|
-
occurredAt?: Date;
|
|
172
|
-
version?: number;
|
|
173
|
-
}): DomainEvent<T, P>;
|
|
118
|
+
declare function hasEntityId<TId, T extends Identifiable<TId>>(entities: T[], id: TId): boolean;
|
|
174
119
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
120
|
+
* Removes an entity with the given ID from the collection.
|
|
121
|
+
* Returns a new array without the entity.
|
|
177
122
|
*
|
|
178
|
-
* @param
|
|
179
|
-
* @param
|
|
180
|
-
* @returns
|
|
123
|
+
* @param entities - Array of entities
|
|
124
|
+
* @param id - The ID of the entity to remove
|
|
125
|
+
* @returns A new array without the entity with the given ID
|
|
181
126
|
*
|
|
182
127
|
* @example
|
|
183
128
|
* ```typescript
|
|
184
|
-
* const
|
|
185
|
-
* "
|
|
186
|
-
* {
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
129
|
+
* const items: OrderItem[] = [
|
|
130
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
131
|
+
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
132
|
+
* ];
|
|
133
|
+
*
|
|
134
|
+
* const updated = removeEntityById(items, itemId1);
|
|
135
|
+
* // updated is [{ id: itemId2, productId: "prod-2", quantity: 1 }]
|
|
191
136
|
* ```
|
|
192
137
|
*/
|
|
193
|
-
declare function
|
|
138
|
+
declare function removeEntityById<TId, T extends Identifiable<TId>>(entities: T[], id: TId): T[];
|
|
194
139
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
140
|
+
* Updates an entity with the given ID in the collection.
|
|
141
|
+
* Returns a new array with the updated entity.
|
|
142
|
+
* If the entity is not found, returns the original array unchanged.
|
|
197
143
|
*
|
|
198
|
-
* @param
|
|
199
|
-
* @
|
|
144
|
+
* @param entities - Array of entities
|
|
145
|
+
* @param id - The ID of the entity to update
|
|
146
|
+
* @param updater - Function that takes the entity and returns the updated entity
|
|
147
|
+
* @returns A new array with the updated entity
|
|
200
148
|
*
|
|
201
149
|
* @example
|
|
202
150
|
* ```typescript
|
|
203
|
-
* const
|
|
204
|
-
* {
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* )
|
|
151
|
+
* const items: OrderItem[] = [
|
|
152
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
153
|
+
* ];
|
|
154
|
+
*
|
|
155
|
+
* const updated = updateEntityById(items, itemId1, (item) => ({
|
|
156
|
+
* ...item,
|
|
157
|
+
* quantity: item.quantity + 1
|
|
158
|
+
* }));
|
|
159
|
+
* // updated is [{ id: itemId1, productId: "prod-1", quantity: 3 }]
|
|
208
160
|
* ```
|
|
209
161
|
*/
|
|
210
|
-
declare function
|
|
162
|
+
declare function updateEntityById<TId, T extends Identifiable<TId>>(entities: T[], id: TId, updater: (entity: T) => T): T[];
|
|
211
163
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
164
|
+
* Replaces an entity with the given ID in the collection.
|
|
165
|
+
* Returns a new array with the replaced entity.
|
|
166
|
+
* If the entity is not found, returns the original array unchanged.
|
|
215
167
|
*
|
|
216
|
-
* @
|
|
168
|
+
* @param entities - Array of entities
|
|
169
|
+
* @param id - The ID of the entity to replace
|
|
170
|
+
* @param replacement - The replacement entity
|
|
171
|
+
* @returns A new array with the replaced entity
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const items: OrderItem[] = [
|
|
176
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
177
|
+
* ];
|
|
178
|
+
*
|
|
179
|
+
* const updated = replaceEntityById(items, itemId1, {
|
|
180
|
+
* id: itemId1,
|
|
181
|
+
* productId: "prod-1",
|
|
182
|
+
* quantity: 5
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
217
185
|
*/
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* The state of the aggregate at the time of the snapshot.
|
|
221
|
-
*/
|
|
222
|
-
state: TState;
|
|
223
|
-
/**
|
|
224
|
-
* The version of the aggregate when the snapshot was taken.
|
|
225
|
-
*/
|
|
226
|
-
version: Version;
|
|
227
|
-
/**
|
|
228
|
-
* Timestamp when the snapshot was created.
|
|
229
|
-
*/
|
|
230
|
-
snapshotAt: Date;
|
|
231
|
-
}
|
|
186
|
+
declare function replaceEntityById<TId, T extends Identifiable<TId>>(entities: T[], id: TId, replacement: T): T[];
|
|
232
187
|
/**
|
|
233
|
-
*
|
|
234
|
-
* Useful for optimistic concurrency control checks.
|
|
188
|
+
* Extracts all IDs from a collection of entities.
|
|
235
189
|
*
|
|
236
|
-
* @param
|
|
237
|
-
* @
|
|
238
|
-
* @returns true if both aggregates have the same ID and version
|
|
190
|
+
* @param entities - Array of entities
|
|
191
|
+
* @returns Array of entity IDs
|
|
239
192
|
*
|
|
240
193
|
* @example
|
|
241
194
|
* ```typescript
|
|
242
|
-
* const
|
|
243
|
-
*
|
|
244
|
-
*
|
|
195
|
+
* const items: OrderItem[] = [
|
|
196
|
+
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
197
|
+
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
198
|
+
* ];
|
|
245
199
|
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* }
|
|
200
|
+
* const ids = entityIds(items);
|
|
201
|
+
* // ids is [itemId1, itemId2]
|
|
249
202
|
* ```
|
|
250
203
|
*/
|
|
251
|
-
declare function
|
|
252
|
-
id: TId;
|
|
253
|
-
version: Version;
|
|
254
|
-
}, b: {
|
|
255
|
-
id: TId;
|
|
256
|
-
version: Version;
|
|
257
|
-
}): boolean;
|
|
204
|
+
declare function entityIds<TId, T extends Identifiable<TId>>(entities: T[]): TId[];
|
|
258
205
|
|
|
259
206
|
/**
|
|
260
|
-
*
|
|
207
|
+
* Marker interface for Aggregate Roots.
|
|
208
|
+
*
|
|
209
|
+
* In Domain-Driven Design, an Aggregate Root is an Entity (the parent Entity of the aggregate).
|
|
210
|
+
* It represents the aggregate externally and is the only object that external code
|
|
211
|
+
* is allowed to hold references to. All access to child entities within the aggregate
|
|
212
|
+
* must go through the Aggregate Root.
|
|
213
|
+
*
|
|
214
|
+
* An Aggregate consists of:
|
|
215
|
+
* - One Aggregate Root (Entity with id + version)
|
|
216
|
+
* - Optional child entities (Entities with id, but no own version)
|
|
217
|
+
* - Optional value objects
|
|
218
|
+
*
|
|
219
|
+
* The Aggregate Root has identity (id) and version for optimistic concurrency control.
|
|
220
|
+
* Child entities exist only within the aggregate boundary and are versioned through
|
|
221
|
+
* the Aggregate Root.
|
|
222
|
+
*
|
|
223
|
+
* @template TId - The type of the aggregate root identifier
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* class Order extends AggregateRoot<OrderState, OrderId> implements IAggregateRoot<OrderId> {
|
|
228
|
+
* // Order is an Aggregate Root (an Entity)
|
|
229
|
+
* // OrderState contains child entities (e.g., OrderItem) and value objects
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
interface IAggregateRoot<TId extends Id<string>> extends Identifiable<TId> {
|
|
234
|
+
/**
|
|
235
|
+
* Unique identifier of the aggregate root entity.
|
|
236
|
+
*/
|
|
237
|
+
readonly id: TId;
|
|
238
|
+
/**
|
|
239
|
+
* Version number for optimistic concurrency control.
|
|
240
|
+
* Incremented on each state change to detect concurrent modifications.
|
|
241
|
+
* This version applies to the entire aggregate, including all child entities.
|
|
242
|
+
*/
|
|
243
|
+
readonly version: Version;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Configuration options for AggregateRoot behavior.
|
|
261
247
|
*/
|
|
262
248
|
interface AggregateConfig {
|
|
263
249
|
/**
|
|
@@ -285,7 +271,7 @@ interface AggregateConfig {
|
|
|
285
271
|
* - State management (containing child entities and value objects)
|
|
286
272
|
* - Snapshot support for performance optimization
|
|
287
273
|
*
|
|
288
|
-
* Implements `
|
|
274
|
+
* Implements `IAggregateRoot<TId>` to mark this as an Aggregate Root Entity.
|
|
289
275
|
*
|
|
290
276
|
* Use this class when you don't need Event Sourcing but still want
|
|
291
277
|
* aggregate patterns with versioning and state management.
|
|
@@ -296,7 +282,7 @@ interface AggregateConfig {
|
|
|
296
282
|
* @example
|
|
297
283
|
* ```typescript
|
|
298
284
|
* // Order is an Aggregate Root (an Entity)
|
|
299
|
-
* class Order extends
|
|
285
|
+
* class Order extends AggregateRoot<OrderState, OrderId> {
|
|
300
286
|
* constructor(id: OrderId, initialState: OrderState) {
|
|
301
287
|
* super(id, initialState);
|
|
302
288
|
* }
|
|
@@ -308,18 +294,45 @@ interface AggregateConfig {
|
|
|
308
294
|
* }
|
|
309
295
|
* ```
|
|
310
296
|
*/
|
|
311
|
-
declare abstract class
|
|
297
|
+
declare abstract class AggregateRoot<TState, TId extends Id<string>> implements IAggregateRoot<TId> {
|
|
312
298
|
readonly id: TId;
|
|
313
299
|
version: Version;
|
|
314
300
|
private readonly _config;
|
|
315
301
|
private readonly _autoVersionBump;
|
|
302
|
+
private _domainEvents;
|
|
316
303
|
get state(): TState;
|
|
304
|
+
/**
|
|
305
|
+
* Returns a read-only list of domain events recorded by this aggregate.
|
|
306
|
+
* These events are side-effects of state changes.
|
|
307
|
+
*/
|
|
308
|
+
get domainEvents(): ReadonlyArray<unknown>;
|
|
309
|
+
/**
|
|
310
|
+
* Clears the list of recorded domain events.
|
|
311
|
+
* Call this after dispatching the events.
|
|
312
|
+
*/
|
|
313
|
+
clearDomainEvents(): void;
|
|
317
314
|
/**
|
|
318
315
|
* The state is 'protected' so that only the subclass can change it.
|
|
319
316
|
* Subclasses can mutate this directly or use helper methods.
|
|
320
317
|
*/
|
|
321
318
|
protected _state: TState;
|
|
322
319
|
protected constructor(id: TId, initialState: TState, config?: AggregateConfig);
|
|
320
|
+
/**
|
|
321
|
+
* Optional validation hook to ensure state invariants.
|
|
322
|
+
* Called during construction, setState, and snapshot restoration.
|
|
323
|
+
* Override this method to implement validation logic.
|
|
324
|
+
*
|
|
325
|
+
* @param state - The state to validate
|
|
326
|
+
* @throws Error if validation fails
|
|
327
|
+
*/
|
|
328
|
+
protected validateState(_state: TState): void;
|
|
329
|
+
/**
|
|
330
|
+
* Adds a domain event to the aggregate's list of changes.
|
|
331
|
+
* Use this to record side-effects that should be published.
|
|
332
|
+
*
|
|
333
|
+
* @param event - The domain event to add
|
|
334
|
+
*/
|
|
335
|
+
protected addDomainEvent(event: unknown): void;
|
|
323
336
|
/**
|
|
324
337
|
* Manually bumps the aggregate version.
|
|
325
338
|
* Call this after state changes for Optimistic Concurrency Control.
|
|
@@ -331,6 +344,7 @@ declare abstract class AggregateBase<TState, TId extends Id<string>> implements
|
|
|
331
344
|
/**
|
|
332
345
|
* Sets the state and optionally bumps the version automatically.
|
|
333
346
|
* This is a convenience method for state mutations.
|
|
347
|
+
* Automatically validates the newState using `validateState()`.
|
|
334
348
|
*
|
|
335
349
|
* @param newState - The new state
|
|
336
350
|
* @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)
|
|
@@ -353,6 +367,7 @@ declare abstract class AggregateBase<TState, TId extends Id<string>> implements
|
|
|
353
367
|
* Restores the aggregate from a snapshot.
|
|
354
368
|
* This is useful for loading aggregates from snapshots instead of
|
|
355
369
|
* rebuilding them from scratch.
|
|
370
|
+
* Validates the restored state.
|
|
356
371
|
*
|
|
357
372
|
* @param snapshot - The snapshot to restore from
|
|
358
373
|
*
|
|
@@ -365,6 +380,41 @@ declare abstract class AggregateBase<TState, TId extends Id<string>> implements
|
|
|
365
380
|
restoreFromSnapshot(snapshot: AggregateSnapshot<TState>): void;
|
|
366
381
|
}
|
|
367
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Interface for Event-Sourced Aggregate Roots.
|
|
385
|
+
* Defines the contract for aggregates that manage state changes via event sourcing.
|
|
386
|
+
*
|
|
387
|
+
* @template TId - The type of the aggregate root identifier
|
|
388
|
+
* @template TEvent - The union type of all domain events
|
|
389
|
+
*/
|
|
390
|
+
interface IAggregateEventSourced<TId extends Id<string>, TEvent extends DomainEvent<string, unknown>> extends IAggregateRoot<TId> {
|
|
391
|
+
/**
|
|
392
|
+
* Returns a read-only list of new, not-yet-persisted events.
|
|
393
|
+
*/
|
|
394
|
+
readonly pendingEvents: ReadonlyArray<TEvent>;
|
|
395
|
+
/**
|
|
396
|
+
* Reconstitutes the aggregate from an event history.
|
|
397
|
+
*
|
|
398
|
+
* @param history - An ordered list of past events
|
|
399
|
+
*/
|
|
400
|
+
loadFromHistory(history: TEvent[]): Result<void, string>;
|
|
401
|
+
/**
|
|
402
|
+
* Clears the list of pending events.
|
|
403
|
+
*/
|
|
404
|
+
clearPendingEvents(): void;
|
|
405
|
+
/**
|
|
406
|
+
* Checks if the aggregate has any pending events.
|
|
407
|
+
*/
|
|
408
|
+
hasPendingEvents(): boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Returns the number of pending events.
|
|
411
|
+
*/
|
|
412
|
+
getEventCount(): number;
|
|
413
|
+
/**
|
|
414
|
+
* Returns the latest pending event, if any.
|
|
415
|
+
*/
|
|
416
|
+
getLatestEvent(): TEvent | undefined;
|
|
417
|
+
}
|
|
368
418
|
type Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;
|
|
369
419
|
/**
|
|
370
420
|
* Extended configuration options for AggregateEventSourced behavior.
|
|
@@ -379,7 +429,7 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
379
429
|
/**
|
|
380
430
|
* Base class for Event-Sourced Aggregate Roots (Entities).
|
|
381
431
|
*
|
|
382
|
-
* Extends `
|
|
432
|
+
* Extends `AggregateRoot` to create an Aggregate Root Entity with Event Sourcing capabilities.
|
|
383
433
|
* The Aggregate Root is the parent Entity of the aggregate and represents it externally.
|
|
384
434
|
*
|
|
385
435
|
* The aggregate state (`TState`) contains:
|
|
@@ -389,7 +439,7 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
389
439
|
* All changes to child entities are versioned through the Aggregate Root. The version
|
|
390
440
|
* applies to the entire aggregate, including all child entities.
|
|
391
441
|
*
|
|
392
|
-
* Extends `
|
|
442
|
+
* Extends `AggregateRoot` with Event Sourcing capabilities:
|
|
393
443
|
* - Event tracking (pendingEvents)
|
|
394
444
|
* - Event handlers for state transitions
|
|
395
445
|
* - Event validation
|
|
@@ -419,10 +469,9 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
|
|
|
419
469
|
* }
|
|
420
470
|
* ```
|
|
421
471
|
*/
|
|
422
|
-
declare abstract class AggregateEventSourced<TState, TEvent extends DomainEvent<string, unknown>, TId extends Id<string>> extends
|
|
472
|
+
declare abstract class AggregateEventSourced<TState, TEvent extends DomainEvent<string, unknown>, TId extends Id<string>> extends AggregateRoot<TState, TId> implements IAggregateEventSourced<TId, TEvent> {
|
|
423
473
|
private readonly _eventConfig;
|
|
424
474
|
private readonly _eventAutoVersionBump;
|
|
425
|
-
private readonly _pendingEvents;
|
|
426
475
|
protected constructor(id: TId, initialState: TState, config?: AggregateEventSourcedConfig);
|
|
427
476
|
/**
|
|
428
477
|
* Returns a read-only list of new, not-yet-persisted events.
|
|
@@ -499,36 +548,246 @@ declare abstract class AggregateEventSourced<TState, TEvent extends DomainEvent<
|
|
|
499
548
|
*/
|
|
500
549
|
getEventCount(): number;
|
|
501
550
|
/**
|
|
502
|
-
* Returns the latest pending event, if any.
|
|
503
|
-
*
|
|
504
|
-
* @returns The most recent event or undefined if no events exist
|
|
551
|
+
* Returns the latest pending event, if any.
|
|
552
|
+
*
|
|
553
|
+
* @returns The most recent event or undefined if no events exist
|
|
554
|
+
*/
|
|
555
|
+
getLatestEvent(): TEvent | undefined;
|
|
556
|
+
/**
|
|
557
|
+
* Restores the aggregate from a snapshot and applies events that occurred after the snapshot.
|
|
558
|
+
* This is more efficient than replaying all events from the beginning.
|
|
559
|
+
*
|
|
560
|
+
* @param snapshot - The snapshot to restore from
|
|
561
|
+
* @param eventsAfterSnapshot - Events that occurred after the snapshot was taken
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```typescript
|
|
565
|
+
* const snapshot = await snapshotRepository.getLatest(aggregateId);
|
|
566
|
+
* const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);
|
|
567
|
+
* aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
restoreFromSnapshotWithEvents(snapshot: AggregateSnapshot<TState>, eventsAfterSnapshot: TEvent[]): Result<void, string>;
|
|
571
|
+
/**
|
|
572
|
+
* A map of event types to their corresponding handlers.
|
|
573
|
+
* Subclasses MUST implement this property.
|
|
574
|
+
*/
|
|
575
|
+
protected abstract readonly handlers: {
|
|
576
|
+
[K in TEvent["type"]]: Handler<TState, Extract<TEvent, {
|
|
577
|
+
type: K;
|
|
578
|
+
}>>;
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
type Version = number & {
|
|
583
|
+
readonly __v: true;
|
|
584
|
+
};
|
|
585
|
+
/**
|
|
586
|
+
* Metadata associated with a domain event for traceability and correlation.
|
|
587
|
+
* Used in event-driven architectures to track event flow across services.
|
|
588
|
+
*/
|
|
589
|
+
interface EventMetadata {
|
|
590
|
+
/**
|
|
591
|
+
* Correlation ID for tracing events across multiple services/components.
|
|
592
|
+
* Typically used to group related events in a distributed system.
|
|
593
|
+
*/
|
|
594
|
+
correlationId?: string;
|
|
595
|
+
/**
|
|
596
|
+
* Causation ID referencing the event or command that caused this event.
|
|
597
|
+
* Used to build event chains and understand causality.
|
|
598
|
+
*/
|
|
599
|
+
causationId?: string;
|
|
600
|
+
/**
|
|
601
|
+
* User ID of the person or system that triggered the event.
|
|
602
|
+
*/
|
|
603
|
+
userId?: string;
|
|
604
|
+
/**
|
|
605
|
+
* Source service or component that produced the event.
|
|
606
|
+
*/
|
|
607
|
+
source?: string;
|
|
608
|
+
/**
|
|
609
|
+
* Additional custom metadata fields.
|
|
610
|
+
* Allows extensibility for domain-specific metadata.
|
|
611
|
+
*/
|
|
612
|
+
[key: string]: unknown;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Domain Event represents something meaningful that happened in the domain.
|
|
616
|
+
* Events are immutable and carry information about what occurred.
|
|
617
|
+
*
|
|
618
|
+
* @template T - The event type name (e.g., "OrderCreated")
|
|
619
|
+
* @template P - The event payload type
|
|
620
|
+
*/
|
|
621
|
+
interface DomainEvent<T extends string, P> {
|
|
622
|
+
/**
|
|
623
|
+
* The type of the event, used for routing and handling.
|
|
624
|
+
*/
|
|
625
|
+
type: T;
|
|
626
|
+
/**
|
|
627
|
+
* The event payload containing the domain data.
|
|
628
|
+
*/
|
|
629
|
+
payload: P;
|
|
630
|
+
/**
|
|
631
|
+
* Timestamp when the event occurred.
|
|
632
|
+
*/
|
|
633
|
+
occurredAt: Date;
|
|
634
|
+
/**
|
|
635
|
+
* Event schema version for handling schema evolution.
|
|
636
|
+
* Defaults to 1 if not specified. Higher versions indicate schema changes.
|
|
637
|
+
*/
|
|
638
|
+
version?: number;
|
|
639
|
+
/**
|
|
640
|
+
* Optional metadata for traceability, correlation, and auditing.
|
|
641
|
+
* Includes correlationId, causationId, userId, source, and custom fields.
|
|
642
|
+
*/
|
|
643
|
+
metadata?: EventMetadata;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Structural interface representing an aggregate with state and events.
|
|
648
|
+
* Used for type constraints in repositories and other infrastructure code.
|
|
649
|
+
*
|
|
650
|
+
* @template State - The type of the aggregate state
|
|
651
|
+
* @template Evt - The union type of all domain events
|
|
652
|
+
*/
|
|
653
|
+
interface Aggregate<State, Evt extends DomainEvent<string, unknown>> {
|
|
654
|
+
state: Readonly<State>;
|
|
655
|
+
version: Version;
|
|
656
|
+
pendingEvents: ReadonlyArray<Evt>;
|
|
657
|
+
}
|
|
658
|
+
declare function aggregate<State, Evt extends DomainEvent<string, unknown>>(state: State, version?: Version): Aggregate<State, Evt>;
|
|
659
|
+
declare function withEvent<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>, evt: E): Aggregate<S, E>;
|
|
660
|
+
declare function bump<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>): Aggregate<S, E>;
|
|
661
|
+
/**
|
|
662
|
+
* Creates a domain event with default values.
|
|
663
|
+
* Sets occurredAt to current date and version to 1 if not provided.
|
|
664
|
+
*
|
|
665
|
+
* @param type - The event type
|
|
666
|
+
* @param payload - The event payload
|
|
667
|
+
* @param options - Optional event configuration
|
|
668
|
+
* @returns A domain event
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```typescript
|
|
672
|
+
* const event = createDomainEvent("OrderCreated", { orderId: "123" });
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
declare function createDomainEvent<T extends string, P>(type: T, payload: P, options?: {
|
|
676
|
+
occurredAt?: Date;
|
|
677
|
+
version?: number;
|
|
678
|
+
metadata?: EventMetadata;
|
|
679
|
+
}): DomainEvent<T, P>;
|
|
680
|
+
/**
|
|
681
|
+
* Creates a domain event with metadata for traceability.
|
|
682
|
+
* Convenience function for creating events with correlation and causation IDs.
|
|
683
|
+
*
|
|
684
|
+
* @param type - The event type
|
|
685
|
+
* @param payload - The event payload
|
|
686
|
+
* @param metadata - Event metadata for traceability
|
|
687
|
+
* @param options - Optional event configuration
|
|
688
|
+
* @returns A domain event with metadata
|
|
689
|
+
*
|
|
690
|
+
* @example
|
|
691
|
+
* ```typescript
|
|
692
|
+
* const event = createDomainEventWithMetadata(
|
|
693
|
+
* "OrderCreated",
|
|
694
|
+
* { orderId: "123" },
|
|
695
|
+
* {
|
|
696
|
+
* correlationId: "corr-123",
|
|
697
|
+
* causationId: "cmd-456",
|
|
698
|
+
* userId: "user-789"
|
|
699
|
+
* }
|
|
700
|
+
* );
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
declare function createDomainEventWithMetadata<T extends string, P>(type: T, payload: P, metadata: EventMetadata, options?: {
|
|
704
|
+
occurredAt?: Date;
|
|
705
|
+
version?: number;
|
|
706
|
+
}): DomainEvent<T, P>;
|
|
707
|
+
/**
|
|
708
|
+
* Copies metadata from a source event to a new event.
|
|
709
|
+
* Useful for maintaining correlation chains in event-driven architectures.
|
|
710
|
+
*
|
|
711
|
+
* @param sourceEvent - The source event to copy metadata from
|
|
712
|
+
* @param additionalMetadata - Additional metadata to merge in
|
|
713
|
+
* @returns Event metadata with copied and merged values
|
|
714
|
+
*
|
|
715
|
+
* @example
|
|
716
|
+
* ```typescript
|
|
717
|
+
* const newEvent = createDomainEvent(
|
|
718
|
+
* "OrderShipped",
|
|
719
|
+
* { orderId: "123" },
|
|
720
|
+
* {
|
|
721
|
+
* metadata: copyMetadata(previousEvent, { causationId: previousEvent.type })
|
|
722
|
+
* }
|
|
723
|
+
* );
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
declare function copyMetadata(sourceEvent: DomainEvent<string, unknown>, additionalMetadata?: Partial<EventMetadata>): EventMetadata;
|
|
727
|
+
/**
|
|
728
|
+
* Merges multiple metadata objects into one.
|
|
729
|
+
* Later metadata objects override earlier ones for the same keys.
|
|
730
|
+
*
|
|
731
|
+
* @param metadataObjects - Array of metadata objects to merge
|
|
732
|
+
* @returns Merged event metadata
|
|
733
|
+
*
|
|
734
|
+
* @example
|
|
735
|
+
* ```typescript
|
|
736
|
+
* const metadata = mergeMetadata(
|
|
737
|
+
* { correlationId: "corr-123" },
|
|
738
|
+
* { userId: "user-456" },
|
|
739
|
+
* { source: "order-service" }
|
|
740
|
+
* );
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
743
|
+
declare function mergeMetadata(...metadataObjects: Array<EventMetadata | undefined>): EventMetadata;
|
|
744
|
+
/**
|
|
745
|
+
* Snapshot of an aggregate state at a specific point in time.
|
|
746
|
+
* Used for optimizing event replay by starting from a snapshot
|
|
747
|
+
* instead of replaying all events from the beginning.
|
|
748
|
+
*
|
|
749
|
+
* @template TState - The type of the aggregate state
|
|
750
|
+
*/
|
|
751
|
+
interface AggregateSnapshot<TState> {
|
|
752
|
+
/**
|
|
753
|
+
* The state of the aggregate at the time of the snapshot.
|
|
505
754
|
*/
|
|
506
|
-
|
|
755
|
+
state: TState;
|
|
507
756
|
/**
|
|
508
|
-
*
|
|
509
|
-
* This is more efficient than replaying all events from the beginning.
|
|
510
|
-
*
|
|
511
|
-
* @param snapshot - The snapshot to restore from
|
|
512
|
-
* @param eventsAfterSnapshot - Events that occurred after the snapshot was taken
|
|
513
|
-
*
|
|
514
|
-
* @example
|
|
515
|
-
* ```typescript
|
|
516
|
-
* const snapshot = await snapshotRepository.getLatest(aggregateId);
|
|
517
|
-
* const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);
|
|
518
|
-
* aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);
|
|
519
|
-
* ```
|
|
757
|
+
* The version of the aggregate when the snapshot was taken.
|
|
520
758
|
*/
|
|
521
|
-
|
|
759
|
+
version: Version;
|
|
522
760
|
/**
|
|
523
|
-
*
|
|
524
|
-
* Subclasses MUST implement this property.
|
|
761
|
+
* Timestamp when the snapshot was created.
|
|
525
762
|
*/
|
|
526
|
-
|
|
527
|
-
[K in TEvent["type"]]: Handler<TState, Extract<TEvent, {
|
|
528
|
-
type: K;
|
|
529
|
-
}>>;
|
|
530
|
-
};
|
|
763
|
+
snapshotAt: Date;
|
|
531
764
|
}
|
|
765
|
+
/**
|
|
766
|
+
* Checks if two aggregates are the same (same ID and version).
|
|
767
|
+
* Useful for optimistic concurrency control checks.
|
|
768
|
+
*
|
|
769
|
+
* @param a - First aggregate
|
|
770
|
+
* @param b - Second aggregate
|
|
771
|
+
* @returns true if both aggregates have the same ID and version
|
|
772
|
+
*
|
|
773
|
+
* @example
|
|
774
|
+
* ```typescript
|
|
775
|
+
* const aggregate1 = await repository.getById(id);
|
|
776
|
+
* // ... some operations ...
|
|
777
|
+
* const aggregate2 = await repository.getById(id);
|
|
778
|
+
*
|
|
779
|
+
* if (!sameAggregate(aggregate1, aggregate2)) {
|
|
780
|
+
* throw new Error("Aggregate was modified by another process");
|
|
781
|
+
* }
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
declare function sameAggregate<TId extends Id<string>>(a: {
|
|
785
|
+
id: TId;
|
|
786
|
+
version: Version;
|
|
787
|
+
}, b: {
|
|
788
|
+
id: TId;
|
|
789
|
+
version: Version;
|
|
790
|
+
}): boolean;
|
|
532
791
|
|
|
533
792
|
/**
|
|
534
793
|
* Marker interface for Commands.
|
|
@@ -959,207 +1218,6 @@ declare class QueryBus<TMap extends QueryTypeMap = QueryTypeMap> implements IQue
|
|
|
959
1218
|
*/
|
|
960
1219
|
declare function guard(cond: boolean, error: string): Result<true, string>;
|
|
961
1220
|
|
|
962
|
-
/**
|
|
963
|
-
* Interface for entities with identity.
|
|
964
|
-
*
|
|
965
|
-
* In Domain-Driven Design, there are two types of entities:
|
|
966
|
-
*
|
|
967
|
-
* 1. **Aggregate Root Entity**: The parent Entity of an aggregate.
|
|
968
|
-
* - Has identity (id) and version
|
|
969
|
-
* - Implemented by classes extending `AggregateBase` or `AggregateEventSourced`
|
|
970
|
-
* - Represents the aggregate externally
|
|
971
|
-
* - Loaded/saved through repositories
|
|
972
|
-
*
|
|
973
|
-
* 2. **Child Entities**: Entities within an aggregate.
|
|
974
|
-
* - Have identity (id), but no own version
|
|
975
|
-
* - Exist only within the aggregate boundary
|
|
976
|
-
* - Versioned through the Aggregate Root
|
|
977
|
-
* - Cannot be referenced directly from outside the aggregate
|
|
978
|
-
*
|
|
979
|
-
* This interface is used for child entities within aggregates. The Aggregate Root
|
|
980
|
-
* also implements this interface (through `AggregateRoot<TId>`), but additionally
|
|
981
|
-
* has version management.
|
|
982
|
-
*
|
|
983
|
-
* @template TId - The type of the entity identifier
|
|
984
|
-
*
|
|
985
|
-
* @example
|
|
986
|
-
* ```typescript
|
|
987
|
-
* // Child Entity within an aggregate
|
|
988
|
-
* type OrderItem = Entity<ItemId> & {
|
|
989
|
-
* productId: string;
|
|
990
|
-
* quantity: number;
|
|
991
|
-
* };
|
|
992
|
-
*
|
|
993
|
-
* // Aggregate Root (also an Entity, but with version)
|
|
994
|
-
* class Order extends AggregateBase<OrderState, OrderId>
|
|
995
|
-
* implements AggregateRoot<OrderId> {
|
|
996
|
-
* // Order is an Entity (the Aggregate Root)
|
|
997
|
-
* // OrderState contains OrderItem (child entities)
|
|
998
|
-
* }
|
|
999
|
-
* ```
|
|
1000
|
-
*/
|
|
1001
|
-
interface Entity<TId> {
|
|
1002
|
-
readonly id: TId;
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Checks if two entities have the same ID.
|
|
1006
|
-
* Works with any object that has an 'id' property.
|
|
1007
|
-
*
|
|
1008
|
-
* @param a - First entity
|
|
1009
|
-
* @param b - Second entity
|
|
1010
|
-
* @returns true if both entities have the same ID, false otherwise
|
|
1011
|
-
*
|
|
1012
|
-
* @example
|
|
1013
|
-
* ```typescript
|
|
1014
|
-
* const item1: OrderItem = { id: itemId1, productId: "prod-1", quantity: 2 };
|
|
1015
|
-
* const item2: OrderItem = { id: itemId2, productId: "prod-2", quantity: 1 };
|
|
1016
|
-
*
|
|
1017
|
-
* sameEntity(item1, item2); // false
|
|
1018
|
-
* sameEntity(item1, item1); // true
|
|
1019
|
-
* ```
|
|
1020
|
-
*/
|
|
1021
|
-
declare function sameEntity<TId>(a: {
|
|
1022
|
-
id: TId;
|
|
1023
|
-
}, b: {
|
|
1024
|
-
id: TId;
|
|
1025
|
-
}): boolean;
|
|
1026
|
-
/**
|
|
1027
|
-
* Finds an entity by ID in a collection.
|
|
1028
|
-
* Returns undefined if not found.
|
|
1029
|
-
*
|
|
1030
|
-
* @param entities - Array of entities to search
|
|
1031
|
-
* @param id - The ID to search for
|
|
1032
|
-
* @returns The entity if found, undefined otherwise
|
|
1033
|
-
*
|
|
1034
|
-
* @example
|
|
1035
|
-
* ```typescript
|
|
1036
|
-
* const items: OrderItem[] = [
|
|
1037
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
1038
|
-
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
1039
|
-
* ];
|
|
1040
|
-
*
|
|
1041
|
-
* const item = findEntityById(items, itemId1);
|
|
1042
|
-
* // item is { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
1043
|
-
* ```
|
|
1044
|
-
*/
|
|
1045
|
-
declare function findEntityById<TId, T extends {
|
|
1046
|
-
id: TId;
|
|
1047
|
-
}>(entities: T[], id: TId): T | undefined;
|
|
1048
|
-
/**
|
|
1049
|
-
* Checks if an entity with the given ID exists in the collection.
|
|
1050
|
-
*
|
|
1051
|
-
* @param entities - Array of entities to search
|
|
1052
|
-
* @param id - The ID to check for
|
|
1053
|
-
* @returns true if an entity with the ID exists, false otherwise
|
|
1054
|
-
*
|
|
1055
|
-
* @example
|
|
1056
|
-
* ```typescript
|
|
1057
|
-
* const items: OrderItem[] = [
|
|
1058
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
1059
|
-
* ];
|
|
1060
|
-
*
|
|
1061
|
-
* hasEntityId(items, itemId1); // true
|
|
1062
|
-
* hasEntityId(items, itemId2); // false
|
|
1063
|
-
* ```
|
|
1064
|
-
*/
|
|
1065
|
-
declare function hasEntityId<TId, T extends {
|
|
1066
|
-
id: TId;
|
|
1067
|
-
}>(entities: T[], id: TId): boolean;
|
|
1068
|
-
/**
|
|
1069
|
-
* Removes an entity with the given ID from the collection.
|
|
1070
|
-
* Returns a new array without the entity.
|
|
1071
|
-
*
|
|
1072
|
-
* @param entities - Array of entities
|
|
1073
|
-
* @param id - The ID of the entity to remove
|
|
1074
|
-
* @returns A new array without the entity with the given ID
|
|
1075
|
-
*
|
|
1076
|
-
* @example
|
|
1077
|
-
* ```typescript
|
|
1078
|
-
* const items: OrderItem[] = [
|
|
1079
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
1080
|
-
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
1081
|
-
* ];
|
|
1082
|
-
*
|
|
1083
|
-
* const updated = removeEntityById(items, itemId1);
|
|
1084
|
-
* // updated is [{ id: itemId2, productId: "prod-2", quantity: 1 }]
|
|
1085
|
-
* ```
|
|
1086
|
-
*/
|
|
1087
|
-
declare function removeEntityById<TId, T extends {
|
|
1088
|
-
id: TId;
|
|
1089
|
-
}>(entities: T[], id: TId): T[];
|
|
1090
|
-
/**
|
|
1091
|
-
* Updates an entity with the given ID in the collection.
|
|
1092
|
-
* Returns a new array with the updated entity.
|
|
1093
|
-
* If the entity is not found, returns the original array unchanged.
|
|
1094
|
-
*
|
|
1095
|
-
* @param entities - Array of entities
|
|
1096
|
-
* @param id - The ID of the entity to update
|
|
1097
|
-
* @param updater - Function that takes the entity and returns the updated entity
|
|
1098
|
-
* @returns A new array with the updated entity
|
|
1099
|
-
*
|
|
1100
|
-
* @example
|
|
1101
|
-
* ```typescript
|
|
1102
|
-
* const items: OrderItem[] = [
|
|
1103
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
1104
|
-
* ];
|
|
1105
|
-
*
|
|
1106
|
-
* const updated = updateEntityById(items, itemId1, (item) => ({
|
|
1107
|
-
* ...item,
|
|
1108
|
-
* quantity: item.quantity + 1
|
|
1109
|
-
* }));
|
|
1110
|
-
* // updated is [{ id: itemId1, productId: "prod-1", quantity: 3 }]
|
|
1111
|
-
* ```
|
|
1112
|
-
*/
|
|
1113
|
-
declare function updateEntityById<TId, T extends {
|
|
1114
|
-
id: TId;
|
|
1115
|
-
}>(entities: T[], id: TId, updater: (entity: T) => T): T[];
|
|
1116
|
-
/**
|
|
1117
|
-
* Replaces an entity with the given ID in the collection.
|
|
1118
|
-
* Returns a new array with the replaced entity.
|
|
1119
|
-
* If the entity is not found, returns the original array unchanged.
|
|
1120
|
-
*
|
|
1121
|
-
* @param entities - Array of entities
|
|
1122
|
-
* @param id - The ID of the entity to replace
|
|
1123
|
-
* @param replacement - The replacement entity
|
|
1124
|
-
* @returns A new array with the replaced entity
|
|
1125
|
-
*
|
|
1126
|
-
* @example
|
|
1127
|
-
* ```typescript
|
|
1128
|
-
* const items: OrderItem[] = [
|
|
1129
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 }
|
|
1130
|
-
* ];
|
|
1131
|
-
*
|
|
1132
|
-
* const updated = replaceEntityById(items, itemId1, {
|
|
1133
|
-
* id: itemId1,
|
|
1134
|
-
* productId: "prod-1",
|
|
1135
|
-
* quantity: 5
|
|
1136
|
-
* });
|
|
1137
|
-
* ```
|
|
1138
|
-
*/
|
|
1139
|
-
declare function replaceEntityById<TId, T extends {
|
|
1140
|
-
id: TId;
|
|
1141
|
-
}>(entities: T[], id: TId, replacement: T): T[];
|
|
1142
|
-
/**
|
|
1143
|
-
* Extracts all IDs from a collection of entities.
|
|
1144
|
-
*
|
|
1145
|
-
* @param entities - Array of entities
|
|
1146
|
-
* @returns Array of entity IDs
|
|
1147
|
-
*
|
|
1148
|
-
* @example
|
|
1149
|
-
* ```typescript
|
|
1150
|
-
* const items: OrderItem[] = [
|
|
1151
|
-
* { id: itemId1, productId: "prod-1", quantity: 2 },
|
|
1152
|
-
* { id: itemId2, productId: "prod-2", quantity: 1 }
|
|
1153
|
-
* ];
|
|
1154
|
-
*
|
|
1155
|
-
* const ids = entityIds(items);
|
|
1156
|
-
* // ids is [itemId1, itemId2]
|
|
1157
|
-
* ```
|
|
1158
|
-
*/
|
|
1159
|
-
declare function entityIds<TId, T extends {
|
|
1160
|
-
id: TId;
|
|
1161
|
-
}>(entities: T[]): TId[];
|
|
1162
|
-
|
|
1163
1221
|
/**
|
|
1164
1222
|
* Simple in-memory event bus implementation.
|
|
1165
1223
|
* Supports multiple subscribers per event type (pub/sub pattern).
|
|
@@ -1215,7 +1273,7 @@ interface ISpecification<T> {
|
|
|
1215
1273
|
* @template TAgg - The aggregate root type (must be an Aggregate Root Entity)
|
|
1216
1274
|
* @template TId - The type of the aggregate root identifier
|
|
1217
1275
|
*/
|
|
1218
|
-
interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends
|
|
1276
|
+
interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends IAggregateRoot<TId> & Aggregate<TState, TEvent>, TId extends Id<string>> {
|
|
1219
1277
|
getById(id: TId): Promise<TAgg | null>;
|
|
1220
1278
|
findOne(spec: ISpecification<TAgg>): Promise<TAgg | null>;
|
|
1221
1279
|
find(spec: ISpecification<TAgg>): Promise<TAgg[]>;
|
|
@@ -1364,13 +1422,46 @@ declare function voWithValidation<T>(t: T, validate: (value: T) => boolean, erro
|
|
|
1364
1422
|
*/
|
|
1365
1423
|
declare function voWithValidationUnsafe<T>(t: T, validate: (value: T) => boolean, errorMessage?: string): VO<T>;
|
|
1366
1424
|
|
|
1425
|
+
/**
|
|
1426
|
+
* Interface for Value Objects.
|
|
1427
|
+
* Value Objects are immutable and defined by their properties.
|
|
1428
|
+
*
|
|
1429
|
+
* @template T - The shape of the value object's properties
|
|
1430
|
+
*/
|
|
1431
|
+
interface IValueObject<T extends object> {
|
|
1432
|
+
/**
|
|
1433
|
+
* The immutable properties of the value object.
|
|
1434
|
+
*/
|
|
1435
|
+
readonly props: Readonly<T>;
|
|
1436
|
+
/**
|
|
1437
|
+
* Checks if this value object is equal to another.
|
|
1438
|
+
* Uses deep equality comparison on the properties.
|
|
1439
|
+
*
|
|
1440
|
+
* @param other - The other value object to compare
|
|
1441
|
+
* @returns true if the properties are deeply equal
|
|
1442
|
+
*/
|
|
1443
|
+
equals(other: IValueObject<T>): boolean;
|
|
1444
|
+
/**
|
|
1445
|
+
* Creates a clone of the value object with optional property overrides.
|
|
1446
|
+
*
|
|
1447
|
+
* @param props - Optional properties to override
|
|
1448
|
+
* @returns A new instance of the value object
|
|
1449
|
+
*/
|
|
1450
|
+
clone(props?: Partial<T>): IValueObject<T>;
|
|
1451
|
+
/**
|
|
1452
|
+
* Serializes the value object to its raw properties for JSON operations.
|
|
1453
|
+
*
|
|
1454
|
+
* @returns The raw properties object
|
|
1455
|
+
*/
|
|
1456
|
+
toJSON(): Readonly<T>;
|
|
1457
|
+
}
|
|
1367
1458
|
/**
|
|
1368
1459
|
* Abstract base class for creating Value Objects.
|
|
1369
1460
|
* Value Objects are immutable and defined by their properties.
|
|
1370
1461
|
*
|
|
1371
1462
|
* @template T - The shape of the value object's properties
|
|
1372
1463
|
*/
|
|
1373
|
-
declare abstract class ValueObject<T extends object> {
|
|
1464
|
+
declare abstract class ValueObject<T extends object> implements IValueObject<T> {
|
|
1374
1465
|
readonly props: Readonly<T>;
|
|
1375
1466
|
/**
|
|
1376
1467
|
* Creates a new ValueObject.
|
|
@@ -1383,18 +1474,43 @@ declare abstract class ValueObject<T extends object> {
|
|
|
1383
1474
|
* constructor(props: { amount: number; currency: string }) {
|
|
1384
1475
|
* super(props);
|
|
1385
1476
|
* }
|
|
1477
|
+
*
|
|
1478
|
+
* protected validate(props: { amount: number; currency: string }): void {
|
|
1479
|
+
* if (props.amount < 0) throw new Error("Amount cannot be negative");
|
|
1480
|
+
* }
|
|
1386
1481
|
* }
|
|
1387
1482
|
* ```
|
|
1388
1483
|
*/
|
|
1389
1484
|
constructor(props: T);
|
|
1485
|
+
/**
|
|
1486
|
+
* Optional validation hook that can be overridden by subclasses.
|
|
1487
|
+
* Should throw an error if validation fails.
|
|
1488
|
+
*
|
|
1489
|
+
* @param props - The properties to validate
|
|
1490
|
+
* @throws Error if validation fails
|
|
1491
|
+
*/
|
|
1492
|
+
protected validate(props: T): void;
|
|
1390
1493
|
/**
|
|
1391
1494
|
* Checks if this value object is equal to another.
|
|
1392
|
-
* Uses deep equality comparison on the properties.
|
|
1495
|
+
* Uses deep equality comparison on the properties and checks for constructor equality.
|
|
1393
1496
|
*
|
|
1394
1497
|
* @param other - The other value object to compare
|
|
1395
|
-
* @returns true if the properties are deeply equal
|
|
1498
|
+
* @returns true if the properties are deeply equal and constructors match
|
|
1396
1499
|
*/
|
|
1397
1500
|
equals(other: ValueObject<T>): boolean;
|
|
1501
|
+
/**
|
|
1502
|
+
* Creates a clone of the value object with optional property overrides.
|
|
1503
|
+
*
|
|
1504
|
+
* @param props - Optional properties to override
|
|
1505
|
+
* @returns A new instance of the value object
|
|
1506
|
+
*/
|
|
1507
|
+
clone(props?: Partial<T>): this;
|
|
1508
|
+
/**
|
|
1509
|
+
* Serializes the value object to its raw properties for JSON operations.
|
|
1510
|
+
*
|
|
1511
|
+
* @returns The raw properties object
|
|
1512
|
+
*/
|
|
1513
|
+
toJSON(): Readonly<T>;
|
|
1398
1514
|
}
|
|
1399
1515
|
|
|
1400
|
-
export { type Aggregate,
|
|
1516
|
+
export { type Aggregate, type AggregateConfig, AggregateEventSourced, type AggregateEventSourcedConfig, AggregateRoot, type AggregateSnapshot, type Clock, type Command, CommandBus, type CommandHandler, type DomainEvent, Entity, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, type IAggregateEventSourced, type IAggregateRoot, type ICommandBus, type IEntity, 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, sameAggregate, sameEntity, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, voWithValidationUnsafe, withCommit, withEvent };
|