@shirudo/ddd-kit 0.8.8 → 0.9.1

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.
@@ -0,0 +1,1328 @@
1
+ type Id<Tag extends string> = string & {
2
+ readonly __brand: Tag;
3
+ };
4
+ interface IdGenerator {
5
+ next: <T extends string>() => Id<T>;
6
+ }
7
+
8
+ type Version = number & {
9
+ readonly __v: true;
10
+ };
11
+ /**
12
+ * Metadata associated with a domain event for traceability and correlation.
13
+ * Used in event-driven architectures to track event flow across services.
14
+ */
15
+ interface EventMetadata {
16
+ /**
17
+ * Correlation ID for tracing events across multiple services/components.
18
+ * Typically used to group related events in a distributed system.
19
+ */
20
+ correlationId?: string;
21
+ /**
22
+ * Causation ID referencing the event or command that caused this event.
23
+ * Used to build event chains and understand causality.
24
+ */
25
+ causationId?: string;
26
+ /**
27
+ * User ID of the person or system that triggered the event.
28
+ */
29
+ userId?: string;
30
+ /**
31
+ * Source service or component that produced the event.
32
+ */
33
+ source?: string;
34
+ /**
35
+ * Additional custom metadata fields.
36
+ * Allows extensibility for domain-specific metadata.
37
+ */
38
+ [key: string]: unknown;
39
+ }
40
+ /**
41
+ * Domain Event represents something meaningful that happened in the domain.
42
+ * Events are immutable and carry information about what occurred.
43
+ *
44
+ * @template T - The event type name (e.g., "OrderCreated")
45
+ * @template P - The event payload type
46
+ */
47
+ interface DomainEvent<T extends string, P> {
48
+ /**
49
+ * The type of the event, used for routing and handling.
50
+ */
51
+ type: T;
52
+ /**
53
+ * The event payload containing the domain data.
54
+ */
55
+ payload: P;
56
+ /**
57
+ * Timestamp when the event occurred.
58
+ */
59
+ occurredAt: Date;
60
+ /**
61
+ * Event schema version for handling schema evolution.
62
+ * Defaults to 1 if not specified. Higher versions indicate schema changes.
63
+ */
64
+ version?: number;
65
+ /**
66
+ * Optional metadata for traceability, correlation, and auditing.
67
+ * Includes correlationId, causationId, userId, source, and custom fields.
68
+ */
69
+ metadata?: EventMetadata;
70
+ }
71
+ /**
72
+ * Marker interface for Aggregate Roots.
73
+ * Aggregate Roots are the entry points for modifying aggregates in DDD.
74
+ * They have identity (id) and version for optimistic concurrency control.
75
+ *
76
+ * In Domain-Driven Design, an Aggregate Root is the only entity that external
77
+ * objects are allowed to hold references to. All access to entities within the
78
+ * aggregate must go through the Aggregate Root.
79
+ *
80
+ * @template TId - The type of the aggregate identifier
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
85
+ * // Order is an Aggregate Root
86
+ * }
87
+ * ```
88
+ */
89
+ interface AggregateRoot<TId extends Id<string>> {
90
+ /**
91
+ * Unique identifier of the aggregate root.
92
+ */
93
+ readonly id: TId;
94
+ /**
95
+ * Version number for optimistic concurrency control.
96
+ * Incremented on each state change to detect concurrent modifications.
97
+ */
98
+ readonly version: Version;
99
+ }
100
+ /**
101
+ * Structural interface representing an aggregate with state and events.
102
+ * Used for type constraints in repositories and other infrastructure code.
103
+ *
104
+ * @template State - The type of the aggregate state
105
+ * @template Evt - The union type of all domain events
106
+ */
107
+ interface Aggregate<State, Evt extends DomainEvent<string, unknown>> {
108
+ state: Readonly<State>;
109
+ version: Version;
110
+ pendingEvents: ReadonlyArray<Evt>;
111
+ }
112
+ declare function aggregate<State, Evt extends DomainEvent<string, unknown>>(state: State, version?: Version): Aggregate<State, Evt>;
113
+ declare function withEvent<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>, evt: E): Aggregate<S, E>;
114
+ declare function bump<S, E extends DomainEvent<string, unknown>>(agg: Aggregate<S, E>): Aggregate<S, E>;
115
+ /**
116
+ * Creates a domain event with default values.
117
+ * Sets occurredAt to current date and version to 1 if not provided.
118
+ *
119
+ * @param type - The event type
120
+ * @param payload - The event payload
121
+ * @param options - Optional event configuration
122
+ * @returns A domain event
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const event = createDomainEvent("OrderCreated", { orderId: "123" });
127
+ * ```
128
+ */
129
+ declare function createDomainEvent<T extends string, P>(type: T, payload: P, options?: {
130
+ occurredAt?: Date;
131
+ version?: number;
132
+ metadata?: EventMetadata;
133
+ }): DomainEvent<T, P>;
134
+ /**
135
+ * Creates a domain event with metadata for traceability.
136
+ * Convenience function for creating events with correlation and causation IDs.
137
+ *
138
+ * @param type - The event type
139
+ * @param payload - The event payload
140
+ * @param metadata - Event metadata for traceability
141
+ * @param options - Optional event configuration
142
+ * @returns A domain event with metadata
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * const event = createDomainEventWithMetadata(
147
+ * "OrderCreated",
148
+ * { orderId: "123" },
149
+ * {
150
+ * correlationId: "corr-123",
151
+ * causationId: "cmd-456",
152
+ * userId: "user-789"
153
+ * }
154
+ * );
155
+ * ```
156
+ */
157
+ declare function createDomainEventWithMetadata<T extends string, P>(type: T, payload: P, metadata: EventMetadata, options?: {
158
+ occurredAt?: Date;
159
+ version?: number;
160
+ }): DomainEvent<T, P>;
161
+ /**
162
+ * Copies metadata from a source event to a new event.
163
+ * Useful for maintaining correlation chains in event-driven architectures.
164
+ *
165
+ * @param sourceEvent - The source event to copy metadata from
166
+ * @param additionalMetadata - Additional metadata to merge in
167
+ * @returns Event metadata with copied and merged values
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const newEvent = createDomainEvent(
172
+ * "OrderShipped",
173
+ * { orderId: "123" },
174
+ * {
175
+ * metadata: copyMetadata(previousEvent, { causationId: previousEvent.type })
176
+ * }
177
+ * );
178
+ * ```
179
+ */
180
+ declare function copyMetadata(sourceEvent: DomainEvent<string, unknown>, additionalMetadata?: Partial<EventMetadata>): EventMetadata;
181
+ /**
182
+ * Merges multiple metadata objects into one.
183
+ * Later metadata objects override earlier ones for the same keys.
184
+ *
185
+ * @param metadataObjects - Array of metadata objects to merge
186
+ * @returns Merged event metadata
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const metadata = mergeMetadata(
191
+ * { correlationId: "corr-123" },
192
+ * { userId: "user-456" },
193
+ * { source: "order-service" }
194
+ * );
195
+ * ```
196
+ */
197
+ declare function mergeMetadata(...metadataObjects: Array<EventMetadata | undefined>): EventMetadata;
198
+ /**
199
+ * Snapshot of an aggregate state at a specific point in time.
200
+ * Used for optimizing event replay by starting from a snapshot
201
+ * instead of replaying all events from the beginning.
202
+ *
203
+ * @template TState - The type of the aggregate state
204
+ */
205
+ interface AggregateSnapshot<TState> {
206
+ /**
207
+ * The state of the aggregate at the time of the snapshot.
208
+ */
209
+ state: TState;
210
+ /**
211
+ * The version of the aggregate when the snapshot was taken.
212
+ */
213
+ version: Version;
214
+ /**
215
+ * Timestamp when the snapshot was created.
216
+ */
217
+ snapshotAt: Date;
218
+ }
219
+ /**
220
+ * Checks if two aggregates are the same (same ID and version).
221
+ * Useful for optimistic concurrency control checks.
222
+ *
223
+ * @param a - First aggregate
224
+ * @param b - Second aggregate
225
+ * @returns true if both aggregates have the same ID and version
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * const aggregate1 = await repository.getById(id);
230
+ * // ... some operations ...
231
+ * const aggregate2 = await repository.getById(id);
232
+ *
233
+ * if (!sameAggregate(aggregate1, aggregate2)) {
234
+ * throw new Error("Aggregate was modified by another process");
235
+ * }
236
+ * ```
237
+ */
238
+ declare function sameAggregate<TId extends Id<string>>(a: {
239
+ id: TId;
240
+ version: Version;
241
+ }, b: {
242
+ id: TId;
243
+ version: Version;
244
+ }): boolean;
245
+
246
+ /**
247
+ * Configuration options for AggregateBase behavior.
248
+ */
249
+ interface AggregateConfig {
250
+ /**
251
+ * Whether to automatically bump the version when state changes.
252
+ * Defaults to false. Set to true for automatic versioning.
253
+ */
254
+ autoVersionBump?: boolean;
255
+ }
256
+ /**
257
+ * Base class for Aggregates without Event Sourcing.
258
+ * Provides core functionality for aggregates:
259
+ * - ID and Version management (for Optimistic Concurrency Control)
260
+ * - State management
261
+ * - Snapshot support for performance optimization
262
+ *
263
+ * Implements `AggregateRoot<TId>` to explicitly mark this as an Aggregate Root
264
+ * in Domain-Driven Design terms.
265
+ *
266
+ * Use this class when you don't need Event Sourcing but still want
267
+ * aggregate patterns with versioning and state management.
268
+ *
269
+ * @template TState - The type of the aggregate state
270
+ * @template TId - The type of the aggregate identifier
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
275
+ * constructor(id: OrderId, initialState: OrderState) {
276
+ * super(id, initialState);
277
+ * }
278
+ *
279
+ * confirm(): void {
280
+ * this._state = { ...this._state, status: "confirmed" };
281
+ * this.bumpVersion();
282
+ * }
283
+ * }
284
+ * ```
285
+ */
286
+ declare abstract class AggregateBase<TState, TId extends Id<string>> implements AggregateRoot<TId> {
287
+ readonly id: TId;
288
+ version: Version;
289
+ private readonly _config;
290
+ private readonly _autoVersionBump;
291
+ get state(): TState;
292
+ /**
293
+ * The state is 'protected' so that only the subclass can change it.
294
+ * Subclasses can mutate this directly or use helper methods.
295
+ */
296
+ protected _state: TState;
297
+ protected constructor(id: TId, initialState: TState, config?: AggregateConfig);
298
+ /**
299
+ * Manually bumps the aggregate version.
300
+ * Call this after state changes for Optimistic Concurrency Control.
301
+ *
302
+ * If `autoVersionBump` is enabled, this is called automatically
303
+ * when using `setState()`.
304
+ */
305
+ protected bumpVersion(): void;
306
+ /**
307
+ * Sets the state and optionally bumps the version automatically.
308
+ * This is a convenience method for state mutations.
309
+ *
310
+ * @param newState - The new state
311
+ * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)
312
+ */
313
+ protected setState(newState: TState, bumpVersion?: boolean): void;
314
+ /**
315
+ * Creates a snapshot of the current aggregate state.
316
+ * Useful for performance optimization, backup/restore, and audit trails.
317
+ *
318
+ * @returns A snapshot containing the current state and version
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * const snapshot = aggregate.createSnapshot();
323
+ * await snapshotRepository.save(aggregate.id, snapshot);
324
+ * ```
325
+ */
326
+ createSnapshot(): AggregateSnapshot<TState>;
327
+ /**
328
+ * Restores the aggregate from a snapshot.
329
+ * This is useful for loading aggregates from snapshots instead of
330
+ * rebuilding them from scratch.
331
+ *
332
+ * @param snapshot - The snapshot to restore from
333
+ *
334
+ * @example
335
+ * ```typescript
336
+ * const snapshot = await snapshotRepository.getLatest(aggregateId);
337
+ * aggregate.restoreFromSnapshot(snapshot);
338
+ * ```
339
+ */
340
+ restoreFromSnapshot(snapshot: AggregateSnapshot<TState>): void;
341
+ }
342
+
343
+ type Ok<T> = {
344
+ ok: true;
345
+ value: T;
346
+ };
347
+ type Err<E> = {
348
+ ok: false;
349
+ error: E;
350
+ };
351
+ type Result<T, E> = Ok<T> | Err<E>;
352
+ /**
353
+ * Creates an Ok result with a value.
354
+ *
355
+ * @param value - The success value
356
+ * @returns An Ok result containing the value
357
+ *
358
+ * @example
359
+ * ```typescript
360
+ * const result = ok(42);
361
+ * // result is Ok<number>
362
+ * ```
363
+ */
364
+ declare function ok<T>(value: T): Ok<T>;
365
+ /**
366
+ * Creates an Ok result with void (no value).
367
+ *
368
+ * @returns An Ok<void> result
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * const result = ok();
373
+ * // result is Ok<void>
374
+ * ```
375
+ */
376
+ declare function ok(): Ok<void>;
377
+ /**
378
+ * Creates an Err result with an error.
379
+ *
380
+ * @param error - The error value
381
+ * @returns An Err result containing the error
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * const result = err("Something went wrong");
386
+ * // result is Err<string>
387
+ * ```
388
+ */
389
+ declare function err<E>(error: E): Err<E>;
390
+ /**
391
+ * Creates an Err result with void (no error value).
392
+ *
393
+ * @returns An Err<void> result
394
+ *
395
+ * @example
396
+ * ```typescript
397
+ * const result = err();
398
+ * // result is Err<void>
399
+ * ```
400
+ */
401
+ declare function err(): Err<void>;
402
+ /**
403
+ * Type guard to check if a Result is Ok.
404
+ * Narrows the type to Ok<T> when returning true.
405
+ *
406
+ * @param result - The result to check
407
+ * @returns true if the result is Ok, false otherwise
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * const result = voWithValidation(data, validator);
412
+ * if (isOk(result)) {
413
+ * // TypeScript knows result is Ok<ValueObject<T>>
414
+ * console.log(result.value);
415
+ * }
416
+ * ```
417
+ */
418
+ declare function isOk<T, E>(result: Result<T, E>): result is Ok<T>;
419
+ /**
420
+ * Type guard to check if a Result is Err.
421
+ * Narrows the type to Err<E> when returning true.
422
+ *
423
+ * @param result - The result to check
424
+ * @returns true if the result is Err, false otherwise
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * const result = voWithValidation(data, validator);
429
+ * if (isErr(result)) {
430
+ * // TypeScript knows result is Err<string>
431
+ * console.error(result.error);
432
+ * }
433
+ * ```
434
+ */
435
+ declare function isErr<T, E>(result: Result<T, E>): result is Err<E>;
436
+
437
+ type Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;
438
+ /**
439
+ * Extended configuration options for AggregateEventSourced behavior.
440
+ */
441
+ interface AggregateEventSourcedConfig extends AggregateConfig {
442
+ /**
443
+ * Whether to automatically bump the version when applying new events.
444
+ * Defaults to true. Set to false to manually control versioning.
445
+ */
446
+ autoVersionBump?: boolean;
447
+ }
448
+ /**
449
+ * Base class for Event-Sourced Aggregates.
450
+ * Extends `AggregateBase` with Event Sourcing capabilities:
451
+ * - Event tracking (pendingEvents)
452
+ * - Event handlers for state transitions
453
+ * - Event validation
454
+ * - History replay
455
+ *
456
+ * Use this class when you want Event Sourcing with full event tracking
457
+ * and replay capabilities.
458
+ *
459
+ * @template TState - The type of the aggregate state
460
+ * @template TEvent - The union type of all domain events
461
+ * @template TId - The type of the aggregate identifier
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
466
+ * confirm(): void {
467
+ * this.apply(createDomainEvent("OrderConfirmed", {}));
468
+ * }
469
+ *
470
+ * protected readonly handlers = {
471
+ * OrderConfirmed: (state: OrderState): OrderState => ({
472
+ * ...state,
473
+ * status: "confirmed",
474
+ * }),
475
+ * };
476
+ * }
477
+ * ```
478
+ */
479
+ declare abstract class AggregateEventSourced<TState, TEvent extends DomainEvent<string, unknown>, TId extends Id<string>> extends AggregateBase<TState, TId> {
480
+ private readonly _eventConfig;
481
+ private readonly _eventAutoVersionBump;
482
+ private readonly _pendingEvents;
483
+ protected constructor(id: TId, initialState: TState, config?: AggregateEventSourcedConfig);
484
+ /**
485
+ * Returns a read-only list of new, not-yet-persisted events.
486
+ */
487
+ get pendingEvents(): ReadonlyArray<TEvent>;
488
+ /**
489
+ * Clears the list of pending events.
490
+ * Typically called after the events have been persisted.
491
+ */
492
+ clearPendingEvents(): void;
493
+ /**
494
+ * Validates an event before it is applied.
495
+ * Override this method to add custom validation logic.
496
+ * Return `ok(true)` if the event is valid, `err(message)` otherwise.
497
+ *
498
+ * @param event - The event to validate
499
+ * @returns Result indicating if the event is valid
500
+ *
501
+ * @example
502
+ * ```typescript
503
+ * protected validateEvent(event: OrderEvent): Result<true, string> {
504
+ * if (event.type === "OrderShipped" && this.state.status !== "confirmed") {
505
+ * return err("Order must be confirmed before shipping");
506
+ * }
507
+ * return ok(true);
508
+ * }
509
+ * ```
510
+ */
511
+ protected validateEvent(_event: TEvent): Result<true, string>;
512
+ /**
513
+ * Applies an event to change the state and adds it
514
+ * to the list of pending events.
515
+ * Returns a Result type instead of throwing an error.
516
+ *
517
+ * @param event - The domain event to apply
518
+ * @param isNew - Indicates whether the event is new (and needs to be persisted)
519
+ * or if it is being loaded from history
520
+ * @returns Result<void, string> - ok if successful, err with error message if validation fails or handler is missing
521
+ */
522
+ protected apply(event: TEvent, isNew?: boolean): Result<void, string>;
523
+ /**
524
+ * Applies an event to change the state and adds it
525
+ * to the list of pending events.
526
+ * Throws an error if validation fails or handler is missing.
527
+ *
528
+ * @param event - The domain event to apply
529
+ * @param isNew - Indicates whether the event is new (and needs to be persisted)
530
+ * or if it is being loaded from history
531
+ * @throws Error if event validation fails or handler is missing
532
+ */
533
+ protected applyUnsafe(event: TEvent, isNew?: boolean): void;
534
+ /**
535
+ * Manually bumps the aggregate version.
536
+ * Only needed if `autoVersionBump` is disabled.
537
+ */
538
+ protected bumpVersion(): void;
539
+ /**
540
+ * Reconstitutes the aggregate from an event history.
541
+ * Sets the version to the number of events in the history.
542
+ *
543
+ * @param history - An ordered list of past events
544
+ */
545
+ loadFromHistory(history: TEvent[]): Result<void, string>;
546
+ /**
547
+ * Checks if the aggregate has any pending events.
548
+ *
549
+ * @returns true if there are pending events, false otherwise
550
+ */
551
+ hasPendingEvents(): boolean;
552
+ /**
553
+ * Returns the number of pending events.
554
+ *
555
+ * @returns The count of pending events
556
+ */
557
+ getEventCount(): number;
558
+ /**
559
+ * Returns the latest pending event, if any.
560
+ *
561
+ * @returns The most recent event or undefined if no events exist
562
+ */
563
+ getLatestEvent(): TEvent | undefined;
564
+ /**
565
+ * Restores the aggregate from a snapshot and applies events that occurred after the snapshot.
566
+ * This is more efficient than replaying all events from the beginning.
567
+ *
568
+ * @param snapshot - The snapshot to restore from
569
+ * @param eventsAfterSnapshot - Events that occurred after the snapshot was taken
570
+ *
571
+ * @example
572
+ * ```typescript
573
+ * const snapshot = await snapshotRepository.getLatest(aggregateId);
574
+ * const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);
575
+ * aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);
576
+ * ```
577
+ */
578
+ restoreFromSnapshotWithEvents(snapshot: AggregateSnapshot<TState>, eventsAfterSnapshot: TEvent[]): Result<void, string>;
579
+ /**
580
+ * A map of event types to their corresponding handlers.
581
+ * Subclasses MUST implement this property.
582
+ */
583
+ protected abstract readonly handlers: {
584
+ [K in TEvent["type"]]: Handler<TState, Extract<TEvent, {
585
+ type: K;
586
+ }>>;
587
+ };
588
+ }
589
+
590
+ /**
591
+ * Marker interface for Commands.
592
+ * Commands represent write operations that change system state.
593
+ * They should be immutable and contain all data needed to perform the operation.
594
+ *
595
+ * This interface can be used as a type marker even when using external frameworks
596
+ * (e.g., RabbitMQ, AWS SQS) to ensure type safety across different bus implementations.
597
+ *
598
+ * @example
599
+ * ```typescript
600
+ * type CreateOrderCommand = Command & {
601
+ * type: "CreateOrder";
602
+ * customerId: string;
603
+ * items: OrderItem[];
604
+ * };
605
+ * ```
606
+ *
607
+ * @example Using with external frameworks (RabbitMQ, etc.)
608
+ * ```typescript
609
+ * // Define command using Command marker
610
+ * type CreateOrderCommand = Command & {
611
+ * type: "CreateOrder";
612
+ * customerId: string;
613
+ * };
614
+ *
615
+ * // Handler can be typed with CommandHandler even for external frameworks
616
+ * const handler: CommandHandler<CreateOrderCommand, OrderId> = async (cmd) => {
617
+ * // ... handler logic
618
+ * return ok(orderId);
619
+ * };
620
+ *
621
+ * // Register with RabbitMQ or other external bus
622
+ * rabbitMQChannel.consume("order.commands", async (message) => {
623
+ * const command = JSON.parse(message.content) as CreateOrderCommand;
624
+ * const result = await handler(command);
625
+ * // ... handle result
626
+ * });
627
+ * ```
628
+ */
629
+ interface Command {
630
+ readonly type: string;
631
+ }
632
+ /**
633
+ * Handler for executing commands.
634
+ * Commands return Result for explicit error handling.
635
+ * Commands may modify system state and should be idempotent when possible.
636
+ *
637
+ * This type can be used to mark handlers even when using external frameworks
638
+ * (e.g., RabbitMQ, AWS SQS, Kafka) to ensure type safety and consistency.
639
+ *
640
+ * @template C - The command type (must extend Command)
641
+ * @template R - The result type
642
+ *
643
+ * @example
644
+ * ```typescript
645
+ * const handler: CommandHandler<CreateOrderCommand, OrderId> = async (cmd) => {
646
+ * const order = Order.create(cmd.customerId, cmd.items);
647
+ * await repository.save(order);
648
+ * return ok(order.id);
649
+ * };
650
+ * ```
651
+ *
652
+ * @example Using with external frameworks
653
+ * ```typescript
654
+ * // Handler typed with CommandHandler for type safety
655
+ * const createOrderHandler: CommandHandler<CreateOrderCommand, OrderId> = async (cmd) => {
656
+ * // ... handler logic
657
+ * return ok(orderId);
658
+ * };
659
+ *
660
+ * // Can be used with any external bus/framework
661
+ * // RabbitMQ example:
662
+ * rabbitMQChannel.consume("commands", async (msg) => {
663
+ * const command = JSON.parse(msg.content) as CreateOrderCommand;
664
+ * const result = await createOrderHandler(command);
665
+ * // ... handle result
666
+ * });
667
+ *
668
+ * // AWS SQS example:
669
+ * sqs.receiveMessage({ QueueUrl: "..." }, async (err, data) => {
670
+ * const command = JSON.parse(data.Messages[0].Body) as CreateOrderCommand;
671
+ * const result = await createOrderHandler(command);
672
+ * // ... handle result
673
+ * });
674
+ * ```
675
+ */
676
+ type CommandHandler<C extends Command, R> = (cmd: C) => Promise<Result<R, string>>;
677
+
678
+ /**
679
+ * Command Bus interface for dispatching commands to their handlers.
680
+ * Provides a centralized way to execute commands with handler registration.
681
+ *
682
+ * @example
683
+ * ```typescript
684
+ * const bus = new CommandBus();
685
+ * bus.register("CreateOrder", createOrderHandler);
686
+ *
687
+ * const result = await bus.execute({
688
+ * type: "CreateOrder",
689
+ * customerId: "123",
690
+ * items: [...]
691
+ * });
692
+ * ```
693
+ */
694
+ interface ICommandBus {
695
+ /**
696
+ * Executes a command by dispatching it to the registered handler.
697
+ *
698
+ * @param command - The command to execute
699
+ * @returns Result containing the success value or error message
700
+ */
701
+ execute<C extends Command, R>(command: C): Promise<Result<R, string>>;
702
+ /**
703
+ * Registers a handler for a specific command type.
704
+ *
705
+ * @param commandType - The command type to register the handler for
706
+ * @param handler - The handler function for this command type
707
+ */
708
+ register<C extends Command, R>(commandType: C["type"], handler: CommandHandler<C, R>): void;
709
+ }
710
+ /**
711
+ * Simple in-memory command bus implementation.
712
+ * Handlers are stored in a Map and dispatched based on command type.
713
+ *
714
+ * **Note:** This is a basic implementation suitable for development and simple use cases.
715
+ * For production environments, consider implementing or using a more feature-rich bus that includes:
716
+ * - Middleware/Pipeline support (logging, validation, authorization)
717
+ * - Error handling and retry logic
718
+ * - Timeout handling
719
+ * - Metrics and observability
720
+ * - Transaction management
721
+ * - Dead letter queue support
722
+ *
723
+ * The `CommandHandler` type can still be used with external production-grade buses
724
+ * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.
725
+ *
726
+ * @example
727
+ * ```typescript
728
+ * const bus = new CommandBus();
729
+ * bus.register("CreateOrder", async (cmd) => {
730
+ * // ... handler logic
731
+ * return ok(orderId);
732
+ * });
733
+ *
734
+ * const result = await bus.execute({ type: "CreateOrder", ... });
735
+ * ```
736
+ */
737
+ declare class CommandBus implements ICommandBus {
738
+ private readonly handlers;
739
+ register<C extends Command, R>(commandType: C["type"], handler: CommandHandler<C, R>): void;
740
+ execute<C extends Command, R>(command: C): Promise<Result<R, string>>;
741
+ }
742
+
743
+ /**
744
+ * Event handler function type for subscribing to domain events.
745
+ *
746
+ * @template Evt - The type of domain event
747
+ */
748
+ type EventHandler<Evt> = (event: Evt) => Promise<void> | void;
749
+ /**
750
+ * Event Bus interface for publishing and subscribing to domain events.
751
+ * Supports multiple subscribers per event type (pub/sub pattern).
752
+ *
753
+ * @template Evt - The type of domain events
754
+ *
755
+ * @example
756
+ * ```typescript
757
+ * const bus = new EventBus<OrderEvent>();
758
+ *
759
+ * // Subscribe to specific event types
760
+ * bus.subscribe("OrderCreated", async (event) => {
761
+ * await sendEmail(event.payload.customerId);
762
+ * });
763
+ *
764
+ * bus.subscribe("OrderShipped", async (event) => {
765
+ * await updateInventory(event.payload.orderId);
766
+ * });
767
+ *
768
+ * // Publish events
769
+ * await bus.publish([orderCreatedEvent, orderShippedEvent]);
770
+ * ```
771
+ */
772
+ interface EventBus<Evt> {
773
+ /**
774
+ * Publishes events to all subscribed handlers.
775
+ * All handlers for each event type will be called.
776
+ *
777
+ * @param events - Array of events to publish
778
+ */
779
+ publish: (events: ReadonlyArray<Evt>) => Promise<void>;
780
+ /**
781
+ * Subscribes a handler to a specific event type.
782
+ * Multiple handlers can subscribe to the same event type.
783
+ *
784
+ * @param eventType - The event type to subscribe to
785
+ * @param handler - The handler function to call when events of this type are published
786
+ * @returns A function to unsubscribe the handler
787
+ *
788
+ * @example
789
+ * ```typescript
790
+ * const unsubscribe = bus.subscribe("OrderCreated", async (event) => {
791
+ * console.log("Order created:", event.payload.orderId);
792
+ * });
793
+ *
794
+ * // Later: unsubscribe
795
+ * unsubscribe();
796
+ * ```
797
+ */
798
+ subscribe: <T extends Evt>(eventType: string, handler: EventHandler<T>) => () => void;
799
+ }
800
+ interface Outbox<Evt> {
801
+ add: (events: ReadonlyArray<Evt>) => Promise<void>;
802
+ }
803
+ interface Clock {
804
+ now: () => Date;
805
+ }
806
+
807
+ interface UnitOfWork {
808
+ transactional<T>(fn: () => Promise<T>): Promise<T>;
809
+ }
810
+ type RepoProvider<R> = (uow: UnitOfWork) => R;
811
+
812
+ /**
813
+ * Helper function for executing commands within a transaction.
814
+ * Handles event persistence via outbox and optional event bus publishing.
815
+ *
816
+ * @param deps - Dependencies including outbox, optional event bus, and unit of work
817
+ * @param fn - Function that returns result and events
818
+ * @returns The result wrapped in a transaction
819
+ *
820
+ * @example
821
+ * ```typescript
822
+ * const result = await withCommit(
823
+ * { outbox, bus, uow },
824
+ * async () => {
825
+ * const order = Order.create(customerId, items);
826
+ * await repository.save(order);
827
+ * return {
828
+ * result: order.id,
829
+ * events: order.pendingEvents
830
+ * };
831
+ * }
832
+ * );
833
+ * ```
834
+ */
835
+ declare function withCommit<Evt, R>(deps: {
836
+ outbox: Outbox<Evt>;
837
+ bus?: EventBus<Evt>;
838
+ uow: UnitOfWork;
839
+ }, fn: () => Promise<{
840
+ result: R;
841
+ events: ReadonlyArray<Evt>;
842
+ }>): Promise<R>;
843
+
844
+ /**
845
+ * Marker interface for Queries.
846
+ * Queries represent read operations that don't change system state.
847
+ * They should be immutable and contain all data needed to perform the read.
848
+ *
849
+ * This interface can be used as a type marker even when using external frameworks
850
+ * (e.g., RabbitMQ, AWS SQS) to ensure type safety across different bus implementations.
851
+ *
852
+ * @example
853
+ * ```typescript
854
+ * type GetOrderQuery = Query & {
855
+ * type: "GetOrder";
856
+ * orderId: OrderId;
857
+ * };
858
+ * ```
859
+ *
860
+ * @example Using with external frameworks
861
+ * ```typescript
862
+ * type GetOrderQuery = Query & {
863
+ * type: "GetOrder";
864
+ * orderId: OrderId;
865
+ * };
866
+ *
867
+ * // Handler typed with QueryHandler for type safety
868
+ * const handler: QueryHandler<GetOrderQuery, Order | null> = async (query) => {
869
+ * return await repository.getById(query.orderId);
870
+ * };
871
+ *
872
+ * // Can be used with any external framework
873
+ * rabbitMQChannel.consume("queries", async (message) => {
874
+ * const query = JSON.parse(message.content) as GetOrderQuery;
875
+ * const result = await handler(query);
876
+ * // ... handle result
877
+ * });
878
+ * ```
879
+ */
880
+ interface Query {
881
+ readonly type: string;
882
+ }
883
+ /**
884
+ * Handler for executing queries.
885
+ * Queries return data directly (no Result type) as read operations
886
+ * are not expected to fail in normal circumstances.
887
+ * Queries should not modify system state and can be cached.
888
+ *
889
+ * This type can be used to mark handlers even when using external frameworks
890
+ * (e.g., RabbitMQ, AWS SQS, Kafka) to ensure type safety and consistency.
891
+ *
892
+ * @template Q - The query type (must extend Query)
893
+ * @template R - The result type
894
+ *
895
+ * @example
896
+ * ```typescript
897
+ * const handler: QueryHandler<GetOrderQuery, Order | null> = async (query) => {
898
+ * return await repository.getById(query.orderId);
899
+ * };
900
+ * ```
901
+ *
902
+ * @example Using with external frameworks
903
+ * ```typescript
904
+ * // Handler typed with QueryHandler for type safety
905
+ * const getOrderHandler: QueryHandler<GetOrderQuery, Order | null> = async (query) => {
906
+ * return await repository.getById(query.orderId);
907
+ * };
908
+ *
909
+ * // Can be used with any external bus/framework
910
+ * rabbitMQChannel.consume("queries", async (msg) => {
911
+ * const query = JSON.parse(msg.content) as GetOrderQuery;
912
+ * const result = await getOrderHandler(query);
913
+ * // ... handle result
914
+ * });
915
+ * ```
916
+ */
917
+ type QueryHandler<Q extends Query, R> = (query: Q) => Promise<R>;
918
+
919
+ /**
920
+ * Query Bus interface for dispatching queries to their handlers.
921
+ * Provides a centralized way to execute queries with handler registration.
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * const bus = new QueryBus();
926
+ * bus.register("GetOrder", getOrderHandler);
927
+ *
928
+ * const order = await bus.execute({
929
+ * type: "GetOrder",
930
+ * orderId: "123"
931
+ * });
932
+ * ```
933
+ */
934
+ interface IQueryBus {
935
+ /**
936
+ * Executes a query by dispatching it to the registered handler.
937
+ * Returns a Result type instead of throwing an error.
938
+ *
939
+ * @param query - The query to execute
940
+ * @returns Result containing the query result if successful, or an error message if no handler is registered
941
+ */
942
+ execute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;
943
+ /**
944
+ * Executes a query by dispatching it to the registered handler.
945
+ * Throws an error if no handler is registered.
946
+ *
947
+ * @param query - The query to execute
948
+ * @returns The query result
949
+ * @throws Error if no handler is registered for the query type
950
+ */
951
+ executeUnsafe<Q extends Query, R>(query: Q): Promise<R>;
952
+ /**
953
+ * Registers a handler for a specific query type.
954
+ *
955
+ * @param queryType - The query type to register the handler for
956
+ * @param handler - The handler function for this query type
957
+ */
958
+ register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
959
+ }
960
+ /**
961
+ * Simple in-memory query bus implementation.
962
+ * Handlers are stored in a Map and dispatched based on query type.
963
+ *
964
+ * **Note:** This is a basic implementation suitable for development and simple use cases.
965
+ * For production environments, consider implementing or using a more feature-rich bus that includes:
966
+ * - Middleware/Pipeline support (logging, caching, rate limiting)
967
+ * - Error handling
968
+ * - Timeout handling
969
+ * - Metrics and observability
970
+ * - Query result caching
971
+ * - Rate limiting
972
+ *
973
+ * The `QueryHandler` type can still be used with external production-grade buses
974
+ * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.
975
+ *
976
+ * @example
977
+ * ```typescript
978
+ * const bus = new QueryBus();
979
+ * bus.register("GetOrder", async (query) => {
980
+ * return await repository.getById(query.orderId);
981
+ * });
982
+ *
983
+ * const order = await bus.execute({ type: "GetOrder", orderId: "123" });
984
+ * ```
985
+ */
986
+ declare class QueryBus implements IQueryBus {
987
+ private readonly handlers;
988
+ register<Q extends Query, R>(queryType: Q["type"], handler: QueryHandler<Q, R>): void;
989
+ execute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;
990
+ executeUnsafe<Q extends Query, R>(query: Q): Promise<R>;
991
+ }
992
+
993
+ /**
994
+ * Guard function that validates a condition and returns a Result.
995
+ * Returns `ok(true)` if the condition is met, otherwise `err(error)`.
996
+ *
997
+ * @param cond - The condition to check
998
+ * @param error - Error message if condition fails
999
+ * @returns Result<true, string>
1000
+ *
1001
+ * @example
1002
+ * ```typescript
1003
+ * const result = guard(id.length > 0, "ID cannot be empty");
1004
+ * if (!result.ok) {
1005
+ * return err(result.error);
1006
+ * }
1007
+ * ```
1008
+ */
1009
+ declare function guard(cond: boolean, error: string): Result<true, string>;
1010
+
1011
+ /**
1012
+ * Optional interface for entities with identity.
1013
+ * Use this when you need explicit entity types for nested entities
1014
+ * within aggregates or for entities that are not aggregate roots.
1015
+ *
1016
+ * @template TId - The type of the entity identifier
1017
+ *
1018
+ * @example
1019
+ * ```typescript
1020
+ * type OrderItem = Entity<ItemId> & {
1021
+ * productId: string;
1022
+ * quantity: number;
1023
+ * };
1024
+ * ```
1025
+ */
1026
+ interface Entity<TId> {
1027
+ readonly id: TId;
1028
+ }
1029
+ /**
1030
+ * Checks if two entities have the same ID.
1031
+ * Works with any object that has an 'id' property.
1032
+ *
1033
+ * @param a - First entity
1034
+ * @param b - Second entity
1035
+ * @returns true if both entities have the same ID, false otherwise
1036
+ *
1037
+ * @example
1038
+ * ```typescript
1039
+ * const item1: OrderItem = { id: itemId1, productId: "prod-1", quantity: 2 };
1040
+ * const item2: OrderItem = { id: itemId2, productId: "prod-2", quantity: 1 };
1041
+ *
1042
+ * sameEntity(item1, item2); // false
1043
+ * sameEntity(item1, item1); // true
1044
+ * ```
1045
+ */
1046
+ declare function sameEntity<TId>(a: {
1047
+ id: TId;
1048
+ }, b: {
1049
+ id: TId;
1050
+ }): boolean;
1051
+ /**
1052
+ * Finds an entity by ID in a collection.
1053
+ * Returns undefined if not found.
1054
+ *
1055
+ * @param entities - Array of entities to search
1056
+ * @param id - The ID to search for
1057
+ * @returns The entity if found, undefined otherwise
1058
+ *
1059
+ * @example
1060
+ * ```typescript
1061
+ * const items: OrderItem[] = [
1062
+ * { id: itemId1, productId: "prod-1", quantity: 2 },
1063
+ * { id: itemId2, productId: "prod-2", quantity: 1 }
1064
+ * ];
1065
+ *
1066
+ * const item = findEntityById(items, itemId1);
1067
+ * // item is { id: itemId1, productId: "prod-1", quantity: 2 }
1068
+ * ```
1069
+ */
1070
+ declare function findEntityById<TId, T extends {
1071
+ id: TId;
1072
+ }>(entities: T[], id: TId): T | undefined;
1073
+ /**
1074
+ * Checks if an entity with the given ID exists in the collection.
1075
+ *
1076
+ * @param entities - Array of entities to search
1077
+ * @param id - The ID to check for
1078
+ * @returns true if an entity with the ID exists, false otherwise
1079
+ *
1080
+ * @example
1081
+ * ```typescript
1082
+ * const items: OrderItem[] = [
1083
+ * { id: itemId1, productId: "prod-1", quantity: 2 }
1084
+ * ];
1085
+ *
1086
+ * hasEntityId(items, itemId1); // true
1087
+ * hasEntityId(items, itemId2); // false
1088
+ * ```
1089
+ */
1090
+ declare function hasEntityId<TId, T extends {
1091
+ id: TId;
1092
+ }>(entities: T[], id: TId): boolean;
1093
+ /**
1094
+ * Removes an entity with the given ID from the collection.
1095
+ * Returns a new array without the entity.
1096
+ *
1097
+ * @param entities - Array of entities
1098
+ * @param id - The ID of the entity to remove
1099
+ * @returns A new array without the entity with the given ID
1100
+ *
1101
+ * @example
1102
+ * ```typescript
1103
+ * const items: OrderItem[] = [
1104
+ * { id: itemId1, productId: "prod-1", quantity: 2 },
1105
+ * { id: itemId2, productId: "prod-2", quantity: 1 }
1106
+ * ];
1107
+ *
1108
+ * const updated = removeEntityById(items, itemId1);
1109
+ * // updated is [{ id: itemId2, productId: "prod-2", quantity: 1 }]
1110
+ * ```
1111
+ */
1112
+ declare function removeEntityById<TId, T extends {
1113
+ id: TId;
1114
+ }>(entities: T[], id: TId): T[];
1115
+ /**
1116
+ * Updates an entity with the given ID in the collection.
1117
+ * Returns a new array with the updated entity.
1118
+ * If the entity is not found, returns the original array unchanged.
1119
+ *
1120
+ * @param entities - Array of entities
1121
+ * @param id - The ID of the entity to update
1122
+ * @param updater - Function that takes the entity and returns the updated entity
1123
+ * @returns A new array with the updated entity
1124
+ *
1125
+ * @example
1126
+ * ```typescript
1127
+ * const items: OrderItem[] = [
1128
+ * { id: itemId1, productId: "prod-1", quantity: 2 }
1129
+ * ];
1130
+ *
1131
+ * const updated = updateEntityById(items, itemId1, (item) => ({
1132
+ * ...item,
1133
+ * quantity: item.quantity + 1
1134
+ * }));
1135
+ * // updated is [{ id: itemId1, productId: "prod-1", quantity: 3 }]
1136
+ * ```
1137
+ */
1138
+ declare function updateEntityById<TId, T extends {
1139
+ id: TId;
1140
+ }>(entities: T[], id: TId, updater: (entity: T) => T): T[];
1141
+ /**
1142
+ * Replaces an entity with the given ID in the collection.
1143
+ * Returns a new array with the replaced entity.
1144
+ * If the entity is not found, returns the original array unchanged.
1145
+ *
1146
+ * @param entities - Array of entities
1147
+ * @param id - The ID of the entity to replace
1148
+ * @param replacement - The replacement entity
1149
+ * @returns A new array with the replaced entity
1150
+ *
1151
+ * @example
1152
+ * ```typescript
1153
+ * const items: OrderItem[] = [
1154
+ * { id: itemId1, productId: "prod-1", quantity: 2 }
1155
+ * ];
1156
+ *
1157
+ * const updated = replaceEntityById(items, itemId1, {
1158
+ * id: itemId1,
1159
+ * productId: "prod-1",
1160
+ * quantity: 5
1161
+ * });
1162
+ * ```
1163
+ */
1164
+ declare function replaceEntityById<TId, T extends {
1165
+ id: TId;
1166
+ }>(entities: T[], id: TId, replacement: T): T[];
1167
+ /**
1168
+ * Extracts all IDs from a collection of entities.
1169
+ *
1170
+ * @param entities - Array of entities
1171
+ * @returns Array of entity IDs
1172
+ *
1173
+ * @example
1174
+ * ```typescript
1175
+ * const items: OrderItem[] = [
1176
+ * { id: itemId1, productId: "prod-1", quantity: 2 },
1177
+ * { id: itemId2, productId: "prod-2", quantity: 1 }
1178
+ * ];
1179
+ *
1180
+ * const ids = entityIds(items);
1181
+ * // ids is [itemId1, itemId2]
1182
+ * ```
1183
+ */
1184
+ declare function entityIds<TId, T extends {
1185
+ id: TId;
1186
+ }>(entities: T[]): TId[];
1187
+
1188
+ /**
1189
+ * Simple in-memory event bus implementation.
1190
+ * Supports multiple subscribers per event type (pub/sub pattern).
1191
+ *
1192
+ * @template Evt - The type of domain events (must extend DomainEvent)
1193
+ *
1194
+ * @example
1195
+ * ```typescript
1196
+ * const bus = new EventBusImpl<OrderEvent>();
1197
+ *
1198
+ * bus.subscribe("OrderCreated", async (event) => {
1199
+ * await sendEmail(event.payload.customerId);
1200
+ * });
1201
+ *
1202
+ * bus.subscribe("OrderCreated", async (event) => {
1203
+ * await logEvent(event);
1204
+ * });
1205
+ *
1206
+ * await bus.publish([orderCreatedEvent]);
1207
+ * // Both handlers will be called
1208
+ * ```
1209
+ */
1210
+ declare class EventBusImpl<Evt extends DomainEvent<string, unknown>> implements EventBus<Evt> {
1211
+ private readonly handlers;
1212
+ subscribe<T extends Evt>(eventType: string, handler: EventHandler<T>): () => void;
1213
+ publish(events: ReadonlyArray<Evt>): Promise<void>;
1214
+ }
1215
+
1216
+ /**
1217
+ * A Specification is a named, standalone object that represents a business rule for a query.
1218
+ * It is "translatable" into a concrete database query.
1219
+ */
1220
+ interface ISpecification<T> {
1221
+ readonly _type: T;
1222
+ }
1223
+
1224
+ /**
1225
+ * The Repository works only with Aggregate Roots.
1226
+ * It encapsulates the complexity of the data source (DB, API, etc.).
1227
+ *
1228
+ * Repositories work with Aggregate Roots, which are the entry points
1229
+ * for modifying aggregates in Domain-Driven Design.
1230
+ *
1231
+ * @template TState - The type of the aggregate state
1232
+ * @template TEvent - The union type of all domain events
1233
+ * @template TAgg - The aggregate type (must be an Aggregate Root)
1234
+ * @template TId - The type of the aggregate identifier
1235
+ */
1236
+ interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends AggregateRoot<TId> & Aggregate<TState, TEvent>, TId extends Id<string>> {
1237
+ getById(id: TId): Promise<TAgg | null>;
1238
+ findOne(spec: ISpecification<TAgg>): Promise<TAgg | null>;
1239
+ find(spec: ISpecification<TAgg>): Promise<TAgg[]>;
1240
+ save(aggregate: TAgg): Promise<void>;
1241
+ delete(id: TId): Promise<void>;
1242
+ }
1243
+
1244
+ type ValueObject<T> = Readonly<T>;
1245
+ /**
1246
+ * Creates a deeply immutable value object from the given data.
1247
+ * All nested objects and arrays are frozen recursively.
1248
+ *
1249
+ * @param t - The data to convert into a value object
1250
+ * @returns A deeply frozen, immutable value object
1251
+ *
1252
+ * @example
1253
+ * ```typescript
1254
+ * const address = vo({
1255
+ * street: "Main St",
1256
+ * city: "Berlin",
1257
+ * coordinates: { lat: 52.5, lng: 13.4 }
1258
+ * });
1259
+ * // address.coordinates.lat = 99; // ❌ Error: Cannot assign to read-only property
1260
+ * ```
1261
+ */
1262
+ declare function vo<T>(t: T): ValueObject<T>;
1263
+ /**
1264
+ * Compares two value objects for equality based on their values.
1265
+ * Uses deep equality comparison by serializing both objects to JSON.
1266
+ *
1267
+ * Note: This is a simple implementation. For production use with complex objects,
1268
+ * consider using a more robust deep equality library or implementing custom equality logic.
1269
+ *
1270
+ * @param a - First value object
1271
+ * @param b - Second value object
1272
+ * @returns true if both objects have the same values, false otherwise
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * const money1 = vo({ amount: 100, currency: "USD" });
1277
+ * const money2 = vo({ amount: 100, currency: "USD" });
1278
+ * voEquals(money1, money2); // true
1279
+ * ```
1280
+ */
1281
+ declare function voEquals<T>(a: ValueObject<T>, b: ValueObject<T>): boolean;
1282
+ /**
1283
+ * Creates a value object with optional validation.
1284
+ * Returns a Result type instead of throwing an error.
1285
+ *
1286
+ * @param t - The data to convert into a value object
1287
+ * @param validate - Validation function that returns true if valid
1288
+ * @param errorMessage - Optional custom error message if validation fails
1289
+ * @returns Result containing the value object if valid, or an error message if validation fails
1290
+ *
1291
+ * @example
1292
+ * ```typescript
1293
+ * const result = voWithValidation(
1294
+ * { amount: 100, currency: "USD" },
1295
+ * (m) => m.amount >= 0 && m.currency.length === 3,
1296
+ * "Invalid money: amount must be non-negative and currency must be 3 characters"
1297
+ * );
1298
+ *
1299
+ * if (result.ok) {
1300
+ * console.log(result.value); // Use the value object
1301
+ * } else {
1302
+ * console.error(result.error); // Handle validation error
1303
+ * }
1304
+ * ```
1305
+ */
1306
+ declare function voWithValidation<T>(t: T, validate: (value: T) => boolean, errorMessage?: string): Result<ValueObject<T>, string>;
1307
+ /**
1308
+ * Creates a value object with optional validation.
1309
+ * Throws an error if validation fails.
1310
+ *
1311
+ * @param t - The data to convert into a value object
1312
+ * @param validate - Validation function that returns true if valid
1313
+ * @param errorMessage - Optional custom error message if validation fails
1314
+ * @returns A deeply frozen, immutable value object
1315
+ * @throws Error if validation fails
1316
+ *
1317
+ * @example
1318
+ * ```typescript
1319
+ * const money = voWithValidationUnsafe(
1320
+ * { amount: 100, currency: "USD" },
1321
+ * (m) => m.amount >= 0 && m.currency.length === 3,
1322
+ * "Invalid money: amount must be non-negative and currency must be 3 characters"
1323
+ * );
1324
+ * ```
1325
+ */
1326
+ declare function voWithValidationUnsafe<T>(t: T, validate: (value: T) => boolean, errorMessage?: string): ValueObject<T>;
1327
+
1328
+ export { type Aggregate, AggregateBase, type AggregateConfig, AggregateEventSourced, type AggregateEventSourcedConfig, type AggregateRoot, type AggregateSnapshot, type Clock, type Command, CommandBus, type CommandHandler, type DomainEvent, type Entity, type Err, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, type ICommandBus, type IQueryBus, type IRepository, type ISpecification, type Id, type IdGenerator, type Ok, type Outbox, type Query, QueryBus, type QueryHandler, type RepoProvider, type Result, type UnitOfWork, type ValueObject, type Version, aggregate, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, entityIds, err, findEntityById, guard, hasEntityId, isErr, isOk, mergeMetadata, ok, removeEntityById, replaceEntityById, sameAggregate, sameEntity, updateEntityById, vo, voEquals, voWithValidation, voWithValidationUnsafe, withCommit, withEvent };