@shirudo/ddd-kit 0.9.0 → 0.9.2

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 CHANGED
@@ -62,17 +62,43 @@ Value Objects are immutable objects that are defined by their attributes rather
62
62
 
63
63
  ### Entities
64
64
 
65
- Entities are objects with unique identity that are defined by their ID rather than their attributes. The optional `Entity<TId>` interface can be used for nested entities within aggregates or entities that are not aggregate roots. Helper functions like `sameEntity()`, `findEntityById()`, and `hasEntityId()` provide utilities for working with entity collections.
65
+ In Domain-Driven Design, there are two types of entities:
66
+
67
+ 1. **Aggregate Root Entity**: The parent Entity of an aggregate.
68
+ - Has identity (id) and version for optimistic concurrency control
69
+ - Represents the aggregate externally
70
+ - Loaded/saved through repositories
71
+ - Created by extending `AggregateBase` or `AggregateEventSourced`
72
+ - Implements `AggregateRoot<TId>`
73
+
74
+ 2. **Child Entities**: Entities within an aggregate.
75
+ - Have identity (id), but no own version
76
+ - Exist only within the aggregate boundary
77
+ - Versioned through the Aggregate Root
78
+ - Cannot be referenced directly from outside the aggregate
79
+ - Use the `Entity<TId>` interface for type safety
80
+
81
+ The `Entity<TId>` interface is used for child entities within aggregates. Helper functions like `sameEntity()`, `findEntityById()`, `hasEntityId()`, `updateEntityById()`, and `removeEntityById()` provide utilities for working with child entity collections.
66
82
 
67
83
  ### Aggregates
68
84
 
69
- Aggregates are clusters of entities and value objects that form a consistency boundary. The library provides two aggregate base classes:
85
+ Aggregates are clusters of entities and value objects that form a consistency boundary. An aggregate consists of:
86
+
87
+ - **One Aggregate Root** (Entity with id + version)
88
+ - **Optional child entities** (Entities with id, but no own version)
89
+ - **Optional value objects** (immutable objects)
90
+
91
+ The Aggregate Root is an Entity (the parent Entity of the aggregate) that represents the aggregate externally. All changes to child entities are versioned through the Aggregate Root. The version applies to the entire aggregate, including all child entities.
70
92
 
71
- - **`AggregateBase<TState, TId>`** - For aggregates without Event Sourcing. Provides ID and version management, state management, and snapshot support. Use this when you don't need Event Sourcing but still want aggregate patterns with optimistic concurrency control.
93
+ The library provides:
72
94
 
73
- - **`AggregateEventSourced<TState, TEvent, TId>`** - For Event-Sourced aggregates. Extends `AggregateBase` with event tracking, event handlers, event validation, and history replay capabilities. Use this when you want full Event Sourcing with event tracking and replay.
95
+ - **`AggregateRoot<TId>`** - Marker interface for Aggregate Root Entities. The Aggregate Root is an Entity with identity (id) and version for optimistic concurrency control. It represents the aggregate externally and is the only object that can be loaded/saved through repositories.
74
96
 
75
- Both classes support automatic versioning (configurable), snapshot creation/restoration, and optimistic concurrency control.
97
+ - **`AggregateBase<TState, TId>`** - Base class for creating Aggregate Root Entities without Event Sourcing. Implements `AggregateRoot<TId>`. The aggregate state (`TState`) contains child entities and value objects. Provides ID and version management, state management, and snapshot support. Use this when you don't need Event Sourcing but still want aggregate patterns with versioning and state management.
98
+
99
+ - **`AggregateEventSourced<TState, TEvent, TId>`** - Base class for Event-Sourced Aggregate Root Entities. Extends `AggregateBase` (and thus implements `AggregateRoot<TId>`). Adds event tracking, event handlers, event validation, and history replay capabilities. Use this when you want full Event Sourcing with event tracking and replay.
100
+
101
+ Both classes support automatic versioning (configurable), snapshot creation/restoration, and optimistic concurrency control. The version applies to the entire aggregate, including all child entities.
76
102
 
77
103
  ### CQRS (Command Query Responsibility Segregation)
78
104
 
@@ -150,6 +176,7 @@ voEquals(money1, money2); // true (value equality, not reference)
150
176
  ```typescript
151
177
  import {
152
178
  AggregateBase,
179
+ type AggregateRoot,
153
180
  type Id,
154
181
  } from "@shirudo/ddd-kit";
155
182
 
@@ -163,7 +190,7 @@ type OrderState = {
163
190
  status: "pending" | "confirmed" | "shipped";
164
191
  };
165
192
 
166
- class Order extends AggregateBase<OrderState, OrderId> {
193
+ class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
167
194
  static create(id: OrderId, customerId: string): Order {
168
195
  const initialState: OrderState = {
169
196
  id,
@@ -221,6 +248,7 @@ console.log(order.state.status); // "shipped"
221
248
  import {
222
249
  AggregateEventSourced,
223
250
  createDomainEvent,
251
+ type AggregateRoot,
224
252
  type Id,
225
253
  type DomainEvent,
226
254
  } from "@shirudo/ddd-kit";
@@ -240,7 +268,7 @@ type OrderShipped = DomainEvent<"OrderShipped", { trackingNumber: string }>;
240
268
 
241
269
  type OrderEvent = OrderCreated | OrderConfirmed | OrderShipped;
242
270
 
243
- class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
271
+ class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> implements AggregateRoot<OrderId> {
244
272
  static create(id: OrderId, customerId: string): Order {
245
273
  const initialState: OrderState = {
246
274
  id,
@@ -359,6 +387,7 @@ import {
359
387
  createDomainEvent,
360
388
  err,
361
389
  ok,
390
+ type AggregateRoot,
362
391
  type Id,
363
392
  type DomainEvent,
364
393
  type Result,
@@ -369,7 +398,7 @@ type OrderState = { id: OrderId; status: "pending" | "confirmed" | "shipped" };
369
398
  type OrderShipped = DomainEvent<"OrderShipped", { trackingNumber: string }>;
370
399
  type OrderEvent = OrderShipped;
371
400
 
372
- class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
401
+ class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> implements AggregateRoot<OrderId> {
373
402
  // Event validation
374
403
  protected validateEvent(event: OrderEvent): Result<true, string> {
375
404
  if (event.type === "OrderShipped" && this.state.status !== "confirmed") {
@@ -707,168 +736,206 @@ const eventV2 = createDomainEvent(
707
736
  );
708
737
  ```
709
738
 
710
- ### Working with Nested Entities
739
+ ### Working with Child Entities
740
+
741
+ An Aggregate Root Entity can contain multiple child entities. Child entities have identity (id) but no own version - they are versioned through the Aggregate Root.
711
742
 
712
743
  ```typescript
713
744
  import {
714
745
  AggregateBase,
715
- createDomainEvent,
716
746
  Entity,
717
747
  findEntityById,
718
748
  hasEntityId,
719
749
  removeEntityById,
750
+ updateEntityById,
720
751
  sameEntity,
752
+ type AggregateRoot,
721
753
  type Id,
722
- type DomainEvent,
723
754
  } from "@shirudo/ddd-kit";
724
755
 
725
756
  type OrderId = Id<"OrderId">;
726
757
  type ItemId = Id<"ItemId">;
727
758
 
728
- // Define nested entity
759
+ // Child Entity within the aggregate (has id, but no own version)
729
760
  type OrderItem = Entity<ItemId> & {
730
761
  productId: string;
731
762
  quantity: number;
732
763
  price: number;
733
764
  };
734
765
 
766
+ // Aggregate state contains child entities
735
767
  type OrderState = {
736
768
  id: OrderId;
737
769
  customerId: string;
738
- items: OrderItem[];
770
+ items: OrderItem[]; // Child entities
739
771
  total: number;
740
772
  };
741
773
 
742
- type ItemAdded = DomainEvent<"ItemAdded", { item: OrderItem }>;
743
- type ItemRemoved = DomainEvent<"ItemRemoved", { itemId: ItemId }>;
744
- type OrderEvent = ItemAdded | ItemRemoved;
745
-
746
- class Order extends AggregateBase<OrderState, OrderEvent, OrderId> {
774
+ // Order is the Aggregate Root (an Entity with id + version)
775
+ class Order extends AggregateBase<OrderState, OrderId>
776
+ implements AggregateRoot<OrderId> {
747
777
  static create(id: OrderId, customerId: string): Order {
748
778
  const initialState: OrderState = {
749
779
  id,
750
780
  customerId,
751
- items: [],
781
+ items: [], // Child entities
752
782
  total: 0,
753
783
  };
754
- const order = new Order(id, initialState);
755
- const result = order.apply(createDomainEvent("OrderCreated", { customerId }) as any);
756
- if (!result.ok) {
757
- throw new Error(result.error);
758
- }
759
- return order;
784
+ return new Order(id, initialState);
760
785
  }
761
786
 
762
- addItem(item: OrderItem): void {
763
- if (hasEntityId(this.state.items, item.id)) {
764
- throw new Error("Item already exists");
765
- }
766
- const result = this.apply(createDomainEvent("ItemAdded", { item }) as ItemAdded);
767
- if (!result.ok) {
768
- throw new Error(result.error);
787
+ // Operations on child entities are versioned through the Aggregate Root
788
+ addItem(productId: string, quantity: number, price: number): ItemId {
789
+ const itemId = `item-${Date.now()}` as ItemId;
790
+ const item: OrderItem = {
791
+ id: itemId,
792
+ productId,
793
+ quantity,
794
+ price,
795
+ };
796
+
797
+ this._state = {
798
+ ...this._state,
799
+ items: [...this._state.items, item],
800
+ total: this._state.total + price * quantity,
801
+ };
802
+ this.bumpVersion(); // Versions the entire aggregate (including child entities)
803
+ return itemId;
804
+ }
805
+
806
+ updateItemQuantity(itemId: ItemId, newQuantity: number): void {
807
+ const item = findEntityById(this._state.items, itemId);
808
+ if (!item) {
809
+ throw new Error("Item not found");
769
810
  }
811
+
812
+ this._state = {
813
+ ...this._state,
814
+ items: updateEntityById(
815
+ this._state.items,
816
+ itemId,
817
+ (i) => ({ ...i, quantity: newQuantity })
818
+ ),
819
+ total: this._state.total - item.price * item.quantity + item.price * newQuantity,
820
+ };
821
+ this.bumpVersion(); // Versions the entire aggregate
770
822
  }
771
823
 
772
824
  removeItem(itemId: ItemId): void {
773
- if (!hasEntityId(this.state.items, itemId)) {
825
+ const item = findEntityById(this._state.items, itemId);
826
+ if (!item) {
774
827
  throw new Error("Item not found");
775
828
  }
776
- const result = this.apply(createDomainEvent("ItemRemoved", { itemId }) as ItemRemoved);
777
- if (!result.ok) {
778
- throw new Error(result.error);
779
- }
829
+
830
+ this._state = {
831
+ ...this._state,
832
+ items: removeEntityById(this._state.items, itemId),
833
+ total: this._state.total - item.price * item.quantity,
834
+ };
835
+ this.bumpVersion(); // Versions the entire aggregate
780
836
  }
781
837
 
782
838
  getItem(itemId: ItemId): OrderItem | undefined {
783
- return findEntityById(this.state.items, itemId);
839
+ return findEntityById(this._state.items, itemId);
784
840
  }
785
-
786
- protected readonly handlers = {
787
- ItemAdded: (state: OrderState, event: ItemAdded): OrderState => ({
788
- ...state,
789
- items: [...state.items, event.payload.item],
790
- total: state.total + event.payload.item.price * event.payload.item.quantity,
791
- }),
792
- ItemRemoved: (state: OrderState, event: ItemRemoved): OrderState => {
793
- const item = findEntityById(state.items, event.payload.itemId);
794
- if (!item) return state;
795
- return {
796
- ...state,
797
- items: removeEntityById(state.items, event.payload.itemId),
798
- total: state.total - item.price * item.quantity,
799
- };
800
- },
801
- };
802
841
  }
803
842
 
804
843
  // Usage
805
- const orderId = "order-123" as OrderId;
806
- const order = Order.create(orderId, "customer-456");
807
-
808
- const item: OrderItem = {
809
- id: "item-1" as ItemId,
810
- productId: "prod-123",
811
- quantity: 2,
812
- price: 10.99,
813
- };
844
+ const order = Order.create("order-123" as OrderId, "customer-456");
845
+ const itemId = order.addItem("product-1", 2, 10.0); // Adds child entity
846
+ order.updateItemQuantity(itemId, 3); // Updates child entity
847
+ order.removeItem(itemId); // Removes child entity
814
848
 
815
- order.addItem(item);
816
- const foundItem = order.getItem(item.id);
817
- console.log(sameEntity(item, foundItem!)); // true
849
+ // All changes version the Aggregate Root (order.version increments)
850
+ console.log(order.version); // 3 (one for each operation)
818
851
  ```
819
852
 
820
853
  ### Using Result Type for Error Handling
821
854
 
855
+ The `Result<T, E>` type provides composition utilities to avoid repetitive `if (isErr)` checks:
856
+
822
857
  ```typescript
823
- import { ok, err, isOk, isErr, type Result, guard } from "@shirudo/ddd-kit";
858
+ import {
859
+ ok,
860
+ err,
861
+ isOk,
862
+ isErr,
863
+ andThen,
864
+ map,
865
+ mapErr,
866
+ unwrapOr,
867
+ unwrapOrElse,
868
+ match,
869
+ type Result,
870
+ guard
871
+ } from "@shirudo/ddd-kit";
824
872
 
825
873
  type UserId = string;
826
874
 
827
875
  function validateUserId(id: string): Result<UserId, string> {
828
- const validation = guard(id.length > 0, "User ID cannot be empty");
829
- if (isErr(validation)) {
830
- return err(validation.error);
831
- }
832
- return ok(id as UserId);
876
+ return id.length > 0 ? ok(id as UserId) : err("User ID cannot be empty");
833
877
  }
834
878
 
835
- function createUser(id: string): Result<{ id: UserId; name: string }, string> {
836
- const userIdResult = validateUserId(id);
837
- if (isErr(userIdResult)) {
838
- return err(userIdResult.error);
839
- }
840
-
841
- return ok({
842
- id: userIdResult.value,
843
- name: "John Doe",
844
- });
879
+ function validateEmail(email: string): Result<string, string> {
880
+ return email.includes("@") ? ok(email) : err("Invalid email");
845
881
  }
846
882
 
847
- // Usage with type guards (recommended)
848
- const result = createUser("user-123");
849
- if (isOk(result)) {
850
- console.log("User created:", result.value); // TypeScript knows result is Ok
851
- } else {
852
- console.error("Error:", result.error); // TypeScript knows result is Err
883
+ // Chaining operations with andThen (avoids if-checks)
884
+ function createUser(id: string, email: string): Result<{ id: UserId; email: string }, string> {
885
+ return andThen(validateUserId(id), (userId) =>
886
+ map(validateEmail(email), (email) => ({
887
+ id: userId,
888
+ email,
889
+ }))
890
+ );
853
891
  }
854
892
 
855
- // Usage with ok property (also works)
856
- const result2 = createUser("user-123");
857
- if (result2.ok) {
893
+ // Using map for transformations
894
+ const result = ok(5);
895
+ const doubled = map(result, x => x * 2); // Ok<10>
896
+
897
+ // Using mapErr to transform errors
898
+ const errorResult = err("not found");
899
+ const mappedError = mapErr(errorResult, e => `Error: ${e}`); // Err<"Error: not found">
900
+
901
+ // Using unwrapOr for defaults
902
+ const userId = unwrapOr(validateUserId(""), "default-id");
903
+
904
+ // Using unwrapOrElse for computed defaults
905
+ const userId2 = unwrapOrElse(validateUserId(""), err => `fallback-${Date.now()}`);
906
+
907
+ // Using match for pattern matching
908
+ const message = match(createUser("user-123", "test@example.com"),
909
+ user => `User created: ${user.id}`,
910
+ error => `Error: ${error}`
911
+ );
912
+
913
+ // Usage with type guards (still works)
914
+ const result2 = createUser("user-123", "test@example.com");
915
+ if (isOk(result2)) {
858
916
  console.log("User created:", result2.value);
859
917
  } else {
860
918
  console.error("Error:", result2.error);
861
919
  }
862
920
  ```
863
921
 
922
+ **Available Composition Utilities:**
923
+ - `andThen<T, E, U>(result, fn)` - Chains Result operations (flatMap/bind). If Ok, applies function; if Err, returns error unchanged.
924
+ - `map<T, E, U>(result, fn)` - Transforms Ok value. If Err, returns error unchanged.
925
+ - `mapErr<T, E, F>(result, fn)` - Transforms Err value. If Ok, returns value unchanged.
926
+ - `unwrapOr<T, E>(result, defaultValue)` - Returns value if Ok, otherwise returns default.
927
+ - `unwrapOrElse<T, E>(result, fn)` - Returns value if Ok, otherwise computes default from error.
928
+ - `match<T, E, R>(result, onOk, onErr)` - Pattern matching. Applies one function if Ok, another if Err.
929
+
864
930
  ## API Documentation
865
931
 
866
932
  This package is written in TypeScript and provides full type definitions. All types and functions are exported from the main entry point. You can explore the available APIs through your IDE's autocomplete or by examining the type definitions in `node_modules/@shirudo/ddd-kit/dist/index.d.ts`.
867
933
 
868
934
  Key exports include:
869
935
  - `vo()`, `voEquals()`, `voWithValidation()`, `voWithValidationUnsafe()` - Value Object utilities
870
- - `AggregateBase<TState, TId>` - Base class for aggregates without Event Sourcing
871
- - `AggregateEventSourced<TState, TEvent, TId>` - Base class for Event-Sourced aggregates
936
+ - `AggregateRoot<TId>` - Marker interface for Aggregate Root Entities
937
+ - `AggregateBase<TState, TId>` - Base class for creating Aggregate Root Entities without Event Sourcing (implements `AggregateRoot<TId>`)
938
+ - `AggregateEventSourced<TState, TEvent, TId>` - Base class for Event-Sourced Aggregate Root Entities (extends `AggregateBase`, implements `AggregateRoot<TId>`)
872
939
  - `AggregateConfig`, `AggregateEventSourcedConfig` - Configuration interfaces
873
940
  - `AggregateSnapshot<TState>` - Snapshot interface for performance optimization
874
941
  - `sameAggregate()` - Aggregate equality helper
@@ -886,7 +953,9 @@ Key exports include:
886
953
  - `EventHandler<Evt>` - Event handler function type
887
954
  - `EventBus.subscribe()` - Subscribe handlers to event types
888
955
  - `EventBus.publish()` - Publish events to all subscribers
889
- - `Result<T, E>`, `ok()`, `err()`, `isOk()`, `isErr()` - Result type and helpers
956
+ - `Result<T, E>`, `ok()`, `err()`, `isOk()`, `isErr()` - Result type and type guards
957
+ - `andThen()`, `map()`, `mapErr()` - Result composition utilities
958
+ - `unwrapOr()`, `unwrapOrElse()`, `match()` - Result unwrapping and pattern matching
890
959
  - `Id<Tag>` - Branded ID type
891
960
  - `IRepository<TState, TEvent, TAgg, TId>` - Repository interface
892
961
  - `ISpecification<T>` - Specification interface
package/dist/index.d.ts CHANGED
@@ -68,6 +68,52 @@ interface DomainEvent<T extends string, P> {
68
68
  */
69
69
  metadata?: EventMetadata;
70
70
  }
71
+ /**
72
+ * Marker interface for Aggregate Roots.
73
+ *
74
+ * In Domain-Driven Design, an Aggregate Root is an Entity (the parent Entity of the aggregate).
75
+ * It represents the aggregate externally and is the only object that external code
76
+ * is allowed to hold references to. All access to child entities within the aggregate
77
+ * must go through the Aggregate Root.
78
+ *
79
+ * An Aggregate consists of:
80
+ * - One Aggregate Root (Entity with id + version)
81
+ * - Optional child entities (Entities with id, but no own version)
82
+ * - Optional value objects
83
+ *
84
+ * The Aggregate Root has identity (id) and version for optimistic concurrency control.
85
+ * Child entities exist only within the aggregate boundary and are versioned through
86
+ * the Aggregate Root.
87
+ *
88
+ * @template TId - The type of the aggregate root identifier
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
93
+ * // Order is an Aggregate Root (an Entity)
94
+ * // OrderState contains child entities (e.g., OrderItem) and value objects
95
+ * }
96
+ * ```
97
+ */
98
+ interface AggregateRoot<TId extends Id<string>> {
99
+ /**
100
+ * Unique identifier of the aggregate root entity.
101
+ */
102
+ readonly id: TId;
103
+ /**
104
+ * Version number for optimistic concurrency control.
105
+ * Incremented on each state change to detect concurrent modifications.
106
+ * This version applies to the entire aggregate, including all child entities.
107
+ */
108
+ readonly version: Version;
109
+ }
110
+ /**
111
+ * Structural interface representing an aggregate with state and events.
112
+ * Used for type constraints in repositories and other infrastructure code.
113
+ *
114
+ * @template State - The type of the aggregate state
115
+ * @template Evt - The union type of all domain events
116
+ */
71
117
  interface Aggregate<State, Evt extends DomainEvent<string, unknown>> {
72
118
  state: Readonly<State>;
73
119
  version: Version;
@@ -218,33 +264,48 @@ interface AggregateConfig {
218
264
  autoVersionBump?: boolean;
219
265
  }
220
266
  /**
221
- * Base class for Aggregates without Event Sourcing.
222
- * Provides core functionality for aggregates:
267
+ * Base class for creating Aggregate Roots (Entities) without Event Sourcing.
268
+ *
269
+ * This class creates an Entity that serves as the Aggregate Root. The Aggregate Root
270
+ * is the parent Entity of the aggregate and represents it externally. It has identity
271
+ * (id) and version for optimistic concurrency control.
272
+ *
273
+ * The aggregate state (`TState`) contains:
274
+ * - Child entities (Entities with id, but no own version)
275
+ * - Value objects (immutable objects)
276
+ *
277
+ * All changes to child entities are versioned through the Aggregate Root. The version
278
+ * applies to the entire aggregate, including all child entities.
279
+ *
280
+ * Provides core functionality:
223
281
  * - ID and Version management (for Optimistic Concurrency Control)
224
- * - State management
282
+ * - State management (containing child entities and value objects)
225
283
  * - Snapshot support for performance optimization
226
284
  *
285
+ * Implements `AggregateRoot<TId>` to mark this as an Aggregate Root Entity.
286
+ *
227
287
  * Use this class when you don't need Event Sourcing but still want
228
288
  * aggregate patterns with versioning and state management.
229
289
  *
230
- * @template TState - The type of the aggregate state
231
- * @template TId - The type of the aggregate identifier
290
+ * @template TState - The type of the aggregate state (contains child entities and value objects)
291
+ * @template TId - The type of the aggregate root identifier
232
292
  *
233
293
  * @example
234
294
  * ```typescript
235
- * class Order extends AggregateBase<OrderState, OrderId> {
295
+ * // Order is an Aggregate Root (an Entity)
296
+ * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {
236
297
  * constructor(id: OrderId, initialState: OrderState) {
237
298
  * super(id, initialState);
238
299
  * }
239
300
  *
240
301
  * confirm(): void {
241
302
  * this._state = { ...this._state, status: "confirmed" };
242
- * this.bumpVersion();
303
+ * this.bumpVersion(); // Versions the entire aggregate
243
304
  * }
244
305
  * }
245
306
  * ```
246
307
  */
247
- declare abstract class AggregateBase<TState, TId extends Id<string>> {
308
+ declare abstract class AggregateBase<TState, TId extends Id<string>> implements AggregateRoot<TId> {
248
309
  readonly id: TId;
249
310
  version: Version;
250
311
  private readonly _config;
@@ -394,6 +455,255 @@ declare function isOk<T, E>(result: Result<T, E>): result is Ok<T>;
394
455
  * ```
395
456
  */
396
457
  declare function isErr<T, E>(result: Result<T, E>): result is Err<E>;
458
+ /**
459
+ * Chains Result operations (flatMap/bind).
460
+ * If the result is Ok, applies the function to the value.
461
+ * If Err, returns the error unchanged.
462
+ *
463
+ * @param result - The result to chain
464
+ * @param fn - Function that takes the Ok value and returns a new Result
465
+ * @returns A new Result
466
+ *
467
+ * @example
468
+ * ```typescript
469
+ * const result = validateUserId("123")
470
+ * .andThen(userId => validateEmail("test@example.com")
471
+ * .map(email => ({ id: userId, email }))
472
+ * );
473
+ * ```
474
+ */
475
+ declare function andThen<T, E, U>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E>;
476
+ /**
477
+ * Transforms the Ok value using a function.
478
+ * If the result is Err, returns the error unchanged.
479
+ *
480
+ * @param result - The result to transform
481
+ * @param fn - Function to transform the Ok value
482
+ * @returns A new Result with transformed value
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * const result = ok(5);
487
+ * const doubled = map(result, x => x * 2); // Ok<10>
488
+ * ```
489
+ */
490
+ declare function map<T, E, U>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;
491
+ /**
492
+ * Transforms the Err value using a function.
493
+ * If the result is Ok, returns the value unchanged.
494
+ *
495
+ * @param result - The result to transform
496
+ * @param fn - Function to transform the Err value
497
+ * @returns A new Result with transformed error
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * const result = err("not found");
502
+ * const mapped = mapErr(result, e => `Error: ${e}`); // Err<"Error: not found">
503
+ * ```
504
+ */
505
+ declare function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;
506
+ /**
507
+ * Returns the value if Ok, otherwise returns the default value.
508
+ *
509
+ * @param result - The result to unwrap
510
+ * @param defaultValue - Default value to return if Err
511
+ * @returns The Ok value or the default value
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * const result = validateUserId("123");
516
+ * const userId = unwrapOr(result, "default-id");
517
+ * ```
518
+ */
519
+ declare function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T;
520
+ /**
521
+ * Returns the value if Ok, otherwise computes default from error.
522
+ *
523
+ * @param result - The result to unwrap
524
+ * @param fn - Function to compute default from error
525
+ * @returns The Ok value or computed default
526
+ *
527
+ * @example
528
+ * ```typescript
529
+ * const result = validateUserId("");
530
+ * const userId = unwrapOrElse(result, err => `fallback-${Date.now()}`);
531
+ * ```
532
+ */
533
+ declare function unwrapOrElse<T, E>(result: Result<T, E>, fn: (error: E) => T): T;
534
+ /**
535
+ * Pattern matching for Result.
536
+ * Applies one function if Ok, another if Err.
537
+ *
538
+ * @param result - The result to match
539
+ * @param onOk - Function to apply if Ok
540
+ * @param onErr - Function to apply if Err
541
+ * @returns The result of applying the appropriate function
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * const message = match(result,
546
+ * value => `Success: ${value}`,
547
+ * error => `Error: ${error}`
548
+ * );
549
+ * ```
550
+ */
551
+ declare function match<T, E, R>(result: Result<T, E>, onOk: (value: T) => R, onErr: (error: E) => R): R;
552
+
553
+ /**
554
+ * Base class for Result with method chaining support.
555
+ * Provides common methods for both Ok and Err classes.
556
+ */
557
+ declare abstract class ResultBase<T, E> {
558
+ protected readonly _result: Result<T, E>;
559
+ protected constructor(result: Result<T, E>);
560
+ /**
561
+ * Returns the value if Ok, otherwise throws an error.
562
+ * If error is an Error instance, throws it directly.
563
+ * Otherwise, wraps the error in a new Error.
564
+ *
565
+ * @throws Error if the result is Err
566
+ */
567
+ unwrap(): T;
568
+ /**
569
+ * Returns the value if Ok, otherwise returns the default value.
570
+ */
571
+ unwrapOr(defaultValue: T): T;
572
+ /**
573
+ * Returns the value if Ok, otherwise computes default from error.
574
+ */
575
+ unwrapOrElse(fn: (error: E) => T): T;
576
+ /**
577
+ * Pattern matching for Result.
578
+ * Applies one function if Ok, another if Err.
579
+ */
580
+ match<R>(onOk: (value: T) => R, onErr: (error: E) => R): R;
581
+ /**
582
+ * Type guard to check if the result is Ok.
583
+ */
584
+ isOk(): this is Success<T>;
585
+ /**
586
+ * Type guard to check if the result is Err.
587
+ */
588
+ isErr(): this is Erroneous<E>;
589
+ /**
590
+ * Gets the underlying Result value.
591
+ */
592
+ get result(): Result<T, E>;
593
+ }
594
+ /**
595
+ * Class representing a successful result with method chaining support.
596
+ * Use this for class-based API with method chaining.
597
+ *
598
+ * @example
599
+ * ```typescript
600
+ * const okResult = Ok(1);
601
+ * const doubled = okResult.map(x => x * 2).unwrap(); // 2
602
+ *
603
+ * const chained = Ok(5)
604
+ * .andThen(x => Ok(x * 2))
605
+ * .map(x => x + 1)
606
+ * .unwrap(); // 11
607
+ * ```
608
+ */
609
+ declare class Success<T> extends ResultBase<T, never> {
610
+ constructor(value: T);
611
+ /**
612
+ * Factory function to create an Ok instance.
613
+ * Can be called with or without `new`.
614
+ */
615
+ static of<T>(value: T): Success<T>;
616
+ /**
617
+ * Chains Result operations (flatMap/bind).
618
+ * If the result is Ok, applies the function to the value.
619
+ * If Err, returns the error unchanged.
620
+ */
621
+ andThen<U, E>(fn: (value: T) => Result<U, E>): Success<U> | Erroneous<E>;
622
+ /**
623
+ * Transforms the Ok value using a function.
624
+ * If the result is Err, returns the error unchanged.
625
+ */
626
+ map<U>(fn: (value: T) => U): Success<U>;
627
+ /**
628
+ * Transforms the Err value using a function.
629
+ * If the result is Ok, returns the value unchanged.
630
+ */
631
+ mapErr<F>(_fn: (error: never) => F): Success<T>;
632
+ }
633
+
634
+ /**
635
+ * Class representing an erroneous result with method chaining support.
636
+ * Use this for class-based API with method chaining.
637
+ *
638
+ * @example
639
+ * ```typescript
640
+ * const errResult = Err(new Error("error"));
641
+ * errResult.map(x => x * 2).unwrap(); // throws Error("error")
642
+ *
643
+ * const mapped = Err("original")
644
+ * .mapErr(e => `Error: ${e}`)
645
+ * .unwrap(); // throws Error("Error: original")
646
+ * ```
647
+ */
648
+ declare class Erroneous<E> extends ResultBase<never, E> {
649
+ constructor(error: E);
650
+ /**
651
+ * Factory function to create an Err instance.
652
+ * Can be called with or without `new`.
653
+ */
654
+ static of<E>(error: E): Erroneous<E>;
655
+ /**
656
+ * Chains Result operations (flatMap/bind).
657
+ * If the result is Ok, applies the function to the value.
658
+ * If Err, returns the error unchanged.
659
+ */
660
+ andThen<U>(_fn: (value: never) => Result<U, E>): Erroneous<E>;
661
+ /**
662
+ * Transforms the Ok value using a function.
663
+ * If the result is Err, returns the error unchanged.
664
+ */
665
+ map<U>(_fn: (value: never) => U): Erroneous<E>;
666
+ /**
667
+ * Transforms the Err value using a function.
668
+ * If the result is Ok, returns the value unchanged.
669
+ */
670
+ mapErr<F>(fn: (error: E) => F): Erroneous<F>;
671
+ }
672
+
673
+ /**
674
+ * Class-based API for Result with method chaining support.
675
+ * Provides an object-oriented alternative to the functional Result type.
676
+ * Use `Outcome.from()` to wrap an existing Result, or `Outcome.ok()` / `Outcome.err()` to create new instances.
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * // Wrap an existing Result
681
+ * const result = Outcome.from(ok(1));
682
+ * const doubled = result.map(x => x * 2).unwrap(); // 2
683
+ *
684
+ * // Create directly
685
+ * const outcome = Outcome.ok(42);
686
+ * const value = outcome.map(x => x + 1).unwrap(); // 43
687
+ * ```
688
+ */
689
+ declare class Outcome<T, E> extends ResultBase<T, E> {
690
+ private constructor();
691
+ /**
692
+ * Creates an Outcome from an Ok value.
693
+ */
694
+ static ok<T>(value: T): Success<T>;
695
+ /**
696
+ * Creates an Outcome from an Err value.
697
+ */
698
+ static err<E>(error: E): Erroneous<E>;
699
+ /**
700
+ * Creates an Outcome from a Result.
701
+ */
702
+ static from<T, E>(result: Result<T, E>): Outcome<T, E>;
703
+ andThen<U>(fn: (value: T) => Result<U, E>): Outcome<U, E>;
704
+ map<U>(fn: (value: T) => U): Outcome<U, E>;
705
+ mapErr<F>(fn: (error: E) => F): Outcome<T, F>;
706
+ }
397
707
 
398
708
  type Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;
399
709
  /**
@@ -407,7 +717,18 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
407
717
  autoVersionBump?: boolean;
408
718
  }
409
719
  /**
410
- * Base class for Event-Sourced Aggregates.
720
+ * Base class for Event-Sourced Aggregate Roots (Entities).
721
+ *
722
+ * Extends `AggregateBase` to create an Aggregate Root Entity with Event Sourcing capabilities.
723
+ * The Aggregate Root is the parent Entity of the aggregate and represents it externally.
724
+ *
725
+ * The aggregate state (`TState`) contains:
726
+ * - Child entities (Entities with id, but no own version)
727
+ * - Value objects (immutable objects)
728
+ *
729
+ * All changes to child entities are versioned through the Aggregate Root. The version
730
+ * applies to the entire aggregate, including all child entities.
731
+ *
411
732
  * Extends `AggregateBase` with Event Sourcing capabilities:
412
733
  * - Event tracking (pendingEvents)
413
734
  * - Event handlers for state transitions
@@ -417,12 +738,13 @@ interface AggregateEventSourcedConfig extends AggregateConfig {
417
738
  * Use this class when you want Event Sourcing with full event tracking
418
739
  * and replay capabilities.
419
740
  *
420
- * @template TState - The type of the aggregate state
741
+ * @template TState - The type of the aggregate state (contains child entities and value objects)
421
742
  * @template TEvent - The union type of all domain events
422
- * @template TId - The type of the aggregate identifier
743
+ * @template TId - The type of the aggregate root identifier
423
744
  *
424
745
  * @example
425
746
  * ```typescript
747
+ * // Order is an Aggregate Root (an Entity) with Event Sourcing
426
748
  * class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {
427
749
  * confirm(): void {
428
750
  * this.apply(createDomainEvent("OrderConfirmed", {}));
@@ -970,18 +1292,42 @@ declare class QueryBus implements IQueryBus {
970
1292
  declare function guard(cond: boolean, error: string): Result<true, string>;
971
1293
 
972
1294
  /**
973
- * Optional interface for entities with identity.
974
- * Use this when you need explicit entity types for nested entities
975
- * within aggregates or for entities that are not aggregate roots.
1295
+ * Interface for entities with identity.
1296
+ *
1297
+ * In Domain-Driven Design, there are two types of entities:
1298
+ *
1299
+ * 1. **Aggregate Root Entity**: The parent Entity of an aggregate.
1300
+ * - Has identity (id) and version
1301
+ * - Implemented by classes extending `AggregateBase` or `AggregateEventSourced`
1302
+ * - Represents the aggregate externally
1303
+ * - Loaded/saved through repositories
1304
+ *
1305
+ * 2. **Child Entities**: Entities within an aggregate.
1306
+ * - Have identity (id), but no own version
1307
+ * - Exist only within the aggregate boundary
1308
+ * - Versioned through the Aggregate Root
1309
+ * - Cannot be referenced directly from outside the aggregate
1310
+ *
1311
+ * This interface is used for child entities within aggregates. The Aggregate Root
1312
+ * also implements this interface (through `AggregateRoot<TId>`), but additionally
1313
+ * has version management.
976
1314
  *
977
1315
  * @template TId - The type of the entity identifier
978
1316
  *
979
1317
  * @example
980
1318
  * ```typescript
1319
+ * // Child Entity within an aggregate
981
1320
  * type OrderItem = Entity<ItemId> & {
982
1321
  * productId: string;
983
1322
  * quantity: number;
984
1323
  * };
1324
+ *
1325
+ * // Aggregate Root (also an Entity, but with version)
1326
+ * class Order extends AggregateBase<OrderState, OrderId>
1327
+ * implements AggregateRoot<OrderId> {
1328
+ * // Order is an Entity (the Aggregate Root)
1329
+ * // OrderState contains OrderItem (child entities)
1330
+ * }
985
1331
  * ```
986
1332
  */
987
1333
  interface Entity<TId> {
@@ -1183,10 +1529,25 @@ interface ISpecification<T> {
1183
1529
  }
1184
1530
 
1185
1531
  /**
1186
- * The Repository works only with Aggregates.
1187
- * It encapsulates the complexity of the data source (DB, API, etc.).
1532
+ * Repository interface for Aggregate Roots (Entities).
1533
+ *
1534
+ * Repositories work exclusively with Aggregate Root Entities. The Aggregate Root
1535
+ * is the Entity that represents the aggregate externally and is the only object
1536
+ * that can be loaded/saved through repositories.
1537
+ *
1538
+ * When loading an Aggregate Root, all child entities and value objects within
1539
+ * the aggregate state are loaded as well. When saving, the entire aggregate
1540
+ * (including all child entities) is persisted as a unit.
1541
+ *
1542
+ * Child entities cannot be loaded or saved independently - they exist only
1543
+ * within the aggregate boundary and are managed through the Aggregate Root.
1544
+ *
1545
+ * @template TState - The type of the aggregate state (contains child entities and value objects)
1546
+ * @template TEvent - The union type of all domain events
1547
+ * @template TAgg - The aggregate root type (must be an Aggregate Root Entity)
1548
+ * @template TId - The type of the aggregate root identifier
1188
1549
  */
1189
- interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends Aggregate<TState, TEvent>, TId extends Id<string>> {
1550
+ interface IRepository<TState, TEvent extends DomainEvent<string, unknown>, TAgg extends AggregateRoot<TId> & Aggregate<TState, TEvent>, TId extends Id<string>> {
1190
1551
  getById(id: TId): Promise<TAgg | null>;
1191
1552
  findOne(spec: ISpecification<TAgg>): Promise<TAgg | null>;
1192
1553
  find(spec: ISpecification<TAgg>): Promise<TAgg[]>;
@@ -1278,4 +1639,4 @@ declare function voWithValidation<T>(t: T, validate: (value: T) => boolean, erro
1278
1639
  */
1279
1640
  declare function voWithValidationUnsafe<T>(t: T, validate: (value: T) => boolean, errorMessage?: string): ValueObject<T>;
1280
1641
 
1281
- export { type Aggregate, AggregateBase, type AggregateConfig, AggregateEventSourced, type AggregateEventSourcedConfig, 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 };
1642
+ 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, Erroneous, type EventBus, EventBusImpl, type EventHandler, type EventMetadata, type ICommandBus, type IQueryBus, type IRepository, type ISpecification, type Id, type IdGenerator, type Ok, type Outbox, Outcome, type Query, QueryBus, type QueryHandler, type RepoProvider, type Result, Success, type UnitOfWork, type ValueObject, type Version, aggregate, andThen, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, entityIds, err, findEntityById, guard, hasEntityId, isErr, isOk, map, mapErr, match, mergeMetadata, ok, removeEntityById, replaceEntityById, sameAggregate, sameEntity, unwrapOr, unwrapOrElse, updateEntityById, vo, voEquals, voWithValidation, voWithValidationUnsafe, withCommit, withEvent };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var c=Object.defineProperty;var o=(t,e)=>c(t,"name",{value:e,configurable:true});function T(t,e=0){return {state:t,version:e,pendingEvents:[]}}o(T,"aggregate");function E(t,e){return {...t,pendingEvents:[...t.pendingEvents,e]}}o(E,"withEvent");function h(t){return {...t,version:t.version+1}}o(h,"bump");function m(t,e,r){return {type:t,payload:e,occurredAt:r?.occurredAt??new Date,version:r?.version??1,metadata:r?.metadata}}o(m,"createDomainEvent");function x(t,e,r,n){return m(t,e,{...n,metadata:r})}o(x,"createDomainEventWithMetadata");function b(t,e){return {...t.metadata??{},...e??{}}}o(b,"copyMetadata");function R(...t){return Object.assign({},...t.filter(Boolean))}o(R,"mergeMetadata");function I(t,e){return t.id===e.id&&t.version===e.version}o(I,"sameAggregate");var d=class{static{o(this,"AggregateBase");}id;version=0;_config;_autoVersionBump;get state(){return this._state}_state;constructor(e,r,n){this.id=e,this._state=r,this._config=n??{},this._autoVersionBump=this._config.autoVersionBump??false;}bumpVersion(){this.version=this.version+1;}setState(e,r){this._state=e,(r??this._autoVersionBump)&&this.bumpVersion();}createSnapshot(){return {state:{...this._state},version:this.version,snapshotAt:new Date}}restoreFromSnapshot(e){this._state=e.state,this.version=e.version;}};function a(t){return {ok:true,value:t}}o(a,"ok");function s(t){return {ok:false,error:t}}o(s,"err");function _(t){return t.ok===true}o(_,"isOk");function w(t){return t.ok===false}o(w,"isErr");var u=class extends d{static{o(this,"AggregateEventSourced");}_eventConfig;_eventAutoVersionBump;_pendingEvents=[];constructor(e,r,n){super(e,r,n),this._eventConfig=n??{},this._eventAutoVersionBump=this._eventConfig.autoVersionBump??true;}get pendingEvents(){return this._pendingEvents}clearPendingEvents(){this._pendingEvents.length=0;}validateEvent(e){return a(true)}apply(e,r=true){let n=this.validateEvent(e);if(!n.ok)return s(`Event validation failed for ${e.type}: ${n.error}`);let i=this.handlers[e.type];return i?(this._state=i(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1)),a()):s(`Missing handler for event type: ${e.type}`)}applyUnsafe(e,r=true){let n=this.validateEvent(e);if(!n.ok)throw new Error(`Event validation failed for ${e.type}: ${n.error}`);let i=this.handlers[e.type];if(!i)throw new Error(`Missing handler for event type: ${e.type}`);this._state=i(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1));}bumpVersion(){this.version=this.version+1;}loadFromHistory(e){for(let r of e){let n=this.apply(r,false);if(!n.ok)return n}return this.version=e.length,a()}hasPendingEvents(){return this._pendingEvents.length>0}getEventCount(){return this._pendingEvents.length}getLatestEvent(){return this._pendingEvents[this._pendingEvents.length-1]}restoreFromSnapshotWithEvents(e,r){this._state=e.state,this.version=e.version;for(let n of r){let i=this.apply(n,false);if(!i.ok)return i}return this.version=e.version+r.length,a()}};var p=class{static{o(this,"CommandBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);return r?r(e):s(`No handler registered for command type: ${e.type}`)}};function N(t,e){return t.uow.transactional(async()=>{let{result:r,events:n}=await e();return await t.outbox.add(n),t.bus&&await t.bus.publish(n),r})}o(N,"withCommit");var l=class{static{o(this,"QueryBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);if(!r)return s(`No handler registered for query type: ${e.type}`);try{let n=await r(e);return a(n)}catch(n){return s(n instanceof Error?n.message:String(n))}}async executeUnsafe(e){let r=this.handlers.get(e.type);if(!r)throw new Error(`No handler registered for query type: ${e.type}`);return r(e)}};function z(t,e){return t?a(true):s(e)}o(z,"guard");function G(t,e){return t.id===e.id}o(G,"sameEntity");function X(t,e){return t.find(r=>r.id===e)}o(X,"findEntityById");function Y(t,e){return t.some(r=>r.id===e)}o(Y,"hasEntityId");function Z(t,e){return t.filter(r=>r.id!==e)}o(Z,"removeEntityById");function ee(t,e,r){return t.map(n=>n.id===e?r(n):n)}o(ee,"updateEntityById");function te(t,e,r){return t.map(n=>n.id===e?r:n)}o(te,"replaceEntityById");function re(t){return t.map(e=>e.id)}o(re,"entityIds");var g=class{static{o(this,"EventBusImpl");}handlers=new Map;subscribe(e,r){let n=e;this.handlers.has(n)||this.handlers.set(n,new Set);let i=this.handlers.get(n);return i.add(r),()=>{i.delete(r),i.size===0&&this.handlers.delete(n);}}async publish(e){for(let r of e){let n=this.handlers.get(r.type);n&&await Promise.all(Array.from(n).map(i=>i(r)));}}};function v(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t))return t;e.add(t);let r=Object.getOwnPropertyNames(t);for(let n of r){let i=t[n];i&&(typeof i=="object"||Array.isArray(i))&&v(i,e);}return Object.freeze(t)}o(v,"deepFreeze");function f(t){return v({...t})}o(f,"vo");function de(t,e){return JSON.stringify(t)===JSON.stringify(e)}o(de,"voEquals");function ue(t,e,r){return e(t)?a(f(t)):s(r??`Validation failed for value object: ${JSON.stringify(t)}`)}o(ue,"voWithValidation");function pe(t,e,r){if(!e(t))throw new Error(r??`Validation failed for value object: ${JSON.stringify(t)}`);return f(t)}o(pe,"voWithValidationUnsafe");export{d as AggregateBase,u as AggregateEventSourced,p as CommandBus,g as EventBusImpl,l as QueryBus,T as aggregate,h as bump,b as copyMetadata,m as createDomainEvent,x as createDomainEventWithMetadata,re as entityIds,s as err,X as findEntityById,z as guard,Y as hasEntityId,w as isErr,_ as isOk,R as mergeMetadata,a as ok,Z as removeEntityById,te as replaceEntityById,I as sameAggregate,G as sameEntity,ee as updateEntityById,f as vo,de as voEquals,ue as voWithValidation,pe as voWithValidationUnsafe,N as withCommit,E as withEvent};//# sourceMappingURL=index.js.map
1
+ var I=Object.defineProperty;var n=(t,e)=>I(t,"name",{value:e,configurable:true});function O(t,e=0){return {state:t,version:e,pendingEvents:[]}}n(O,"aggregate");function A(t,e){return {...t,pendingEvents:[...t.pendingEvents,e]}}n(A,"withEvent");function C(t){return {...t,version:t.version+1}}n(C,"bump");function _(t,e,r){return {type:t,payload:e,occurredAt:r?.occurredAt??new Date,version:r?.version??1,metadata:r?.metadata}}n(_,"createDomainEvent");function U(t,e,r,o){return _(t,e,{...o,metadata:r})}n(U,"createDomainEventWithMetadata");function Q(t,e){return {...t.metadata??{},...e??{}}}n(Q,"copyMetadata");function B(...t){return Object.assign({},...t.filter(Boolean))}n(B,"mergeMetadata");function P(t,e){return t.id===e.id&&t.version===e.version}n(P,"sameAggregate");var p=class{static{n(this,"AggregateBase");}id;version=0;_config;_autoVersionBump;get state(){return this._state}_state;constructor(e,r,o){this.id=e,this._state=r,this._config=o??{},this._autoVersionBump=this._config.autoVersionBump??false;}bumpVersion(){this.version=this.version+1;}setState(e,r){this._state=e,(r??this._autoVersionBump)&&this.bumpVersion();}createSnapshot(){return {state:{...this._state},version:this.version,snapshotAt:new Date}}restoreFromSnapshot(e){this._state=e.state,this.version=e.version;}};function a(t){return {ok:true,value:t}}n(a,"ok");function i(t){return {ok:false,error:t}}n(i,"err");function f(t){return t.ok===true}n(f,"isOk");function v(t){return t.ok===false}n(v,"isErr");function l(t,e){return t.ok?e(t.value):t}n(l,"andThen");function c(t,e){return t.ok?a(e(t.value)):t}n(c,"map");function E(t,e){return t.ok?t:i(e(t.error))}n(E,"mapErr");function m(t,e){return t.ok?t.value:e}n(m,"unwrapOr");function g(t,e){return t.ok?t.value:e(t.error)}n(g,"unwrapOrElse");function h(t,e,r){return t.ok?e(t.value):r(t.error)}n(h,"match");var u=class{static{n(this,"ResultBase");}_result;constructor(e){this._result=e;}unwrap(){if(this._result.ok)return this._result.value;throw this._result.error instanceof Error?this._result.error:new Error(String(this._result.error))}unwrapOr(e){return m(this._result,e)}unwrapOrElse(e){return g(this._result,e)}match(e,r){return h(this._result,e,r)}isOk(){return f(this._result)}isErr(){return v(this._result)}get result(){return this._result}},T=class t extends u{static{n(this,"Success");}constructor(e){super(a(e));}static of(e){return new t(e)}andThen(e){let r=l(this._result,e);return r.ok?new t(r.value):new d(r.error)}map(e){let r=c(this._result,e);if(r.ok)return new t(r.value);throw new Error("Unexpected error in Success.map")}mapErr(e){return this}};var d=class t extends u{static{n(this,"Erroneous");}constructor(e){super(i(e));}static of(e){return new t(e)}andThen(e){return this}map(e){return this}mapErr(e){let r=E(this._result,e);if(!r.ok)return new t(r.error);throw new Error("Unexpected ok in Erroneous.mapErr")}};var y=class t extends u{static{n(this,"Outcome");}constructor(e){super(e);}static ok(e){return new T(e)}static err(e){return new d(e)}static from(e){return new t(e)}andThen(e){return t.from(l(this._result,e))}map(e){return t.from(c(this._result,e))}mapErr(e){return t.from(E(this._result,e))}};var x=class extends p{static{n(this,"AggregateEventSourced");}_eventConfig;_eventAutoVersionBump;_pendingEvents=[];constructor(e,r,o){super(e,r,o),this._eventConfig=o??{},this._eventAutoVersionBump=this._eventConfig.autoVersionBump??true;}get pendingEvents(){return this._pendingEvents}clearPendingEvents(){this._pendingEvents.length=0;}validateEvent(e){return a(true)}apply(e,r=true){let o=this.validateEvent(e);if(!o.ok)return i(`Event validation failed for ${e.type}: ${o.error}`);let s=this.handlers[e.type];return s?(this._state=s(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1)),a()):i(`Missing handler for event type: ${e.type}`)}applyUnsafe(e,r=true){let o=this.validateEvent(e);if(!o.ok)throw new Error(`Event validation failed for ${e.type}: ${o.error}`);let s=this.handlers[e.type];if(!s)throw new Error(`Missing handler for event type: ${e.type}`);this._state=s(this._state,e),r&&(this._pendingEvents.push(e),this._eventAutoVersionBump&&(this.version=this.version+1));}bumpVersion(){this.version=this.version+1;}loadFromHistory(e){for(let r of e){let o=this.apply(r,false);if(!o.ok)return o}return this.version=e.length,a()}hasPendingEvents(){return this._pendingEvents.length>0}getEventCount(){return this._pendingEvents.length}getLatestEvent(){return this._pendingEvents[this._pendingEvents.length-1]}restoreFromSnapshotWithEvents(e,r){this._state=e.state,this.version=e.version;for(let o of r){let s=this.apply(o,false);if(!s.ok)return s}return this.version=e.version+r.length,a()}};var R=class{static{n(this,"CommandBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);return r?r(e):i(`No handler registered for command type: ${e.type}`)}};function re(t,e){return t.uow.transactional(async()=>{let{result:r,events:o}=await e();return await t.outbox.add(o),t.bus&&await t.bus.publish(o),r})}n(re,"withCommit");var w=class{static{n(this,"QueryBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);if(!r)return i(`No handler registered for query type: ${e.type}`);try{let o=await r(e);return a(o)}catch(o){return i(o instanceof Error?o.message:String(o))}}async executeUnsafe(e){let r=this.handlers.get(e.type);if(!r)throw new Error(`No handler registered for query type: ${e.type}`);return r(e)}};function de(t,e){return t?a(true):i(e)}n(de,"guard");function ce(t,e){return t.id===e.id}n(ce,"sameEntity");function Ee(t,e){return t.find(r=>r.id===e)}n(Ee,"findEntityById");function Te(t,e){return t.some(r=>r.id===e)}n(Te,"hasEntityId");function fe(t,e){return t.filter(r=>r.id!==e)}n(fe,"removeEntityById");function ve(t,e,r){return t.map(o=>o.id===e?r(o):o)}n(ve,"updateEntityById");function me(t,e,r){return t.map(o=>o.id===e?r:o)}n(me,"replaceEntityById");function ge(t){return t.map(e=>e.id)}n(ge,"entityIds");var k=class{static{n(this,"EventBusImpl");}handlers=new Map;subscribe(e,r){let o=e;this.handlers.has(o)||this.handlers.set(o,new Set);let s=this.handlers.get(o);return s.add(r),()=>{s.delete(r),s.size===0&&this.handlers.delete(o);}}async publish(e){for(let r of e){let o=this.handlers.get(r.type);o&&await Promise.all(Array.from(o).map(s=>s(r)));}}};function S(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t))return t;e.add(t);let r=Object.getOwnPropertyNames(t);for(let o of r){let s=t[o];s&&(typeof s=="object"||Array.isArray(s))&&S(s,e);}return Object.freeze(t)}n(S,"deepFreeze");function b(t){return S({...t})}n(b,"vo");function ke(t,e){return JSON.stringify(t)===JSON.stringify(e)}n(ke,"voEquals");function Se(t,e,r){return e(t)?a(b(t)):i(r??`Validation failed for value object: ${JSON.stringify(t)}`)}n(Se,"voWithValidation");function be(t,e,r){if(!e(t))throw new Error(r??`Validation failed for value object: ${JSON.stringify(t)}`);return b(t)}n(be,"voWithValidationUnsafe");export{p as AggregateBase,x as AggregateEventSourced,R as CommandBus,d as Erroneous,k as EventBusImpl,y as Outcome,w as QueryBus,T as Success,O as aggregate,l as andThen,C as bump,Q as copyMetadata,_ as createDomainEvent,U as createDomainEventWithMetadata,ge as entityIds,i as err,Ee as findEntityById,de as guard,Te as hasEntityId,v as isErr,f as isOk,c as map,E as mapErr,h as match,B as mergeMetadata,a as ok,fe as removeEntityById,me as replaceEntityById,P as sameAggregate,ce as sameEntity,m as unwrapOr,g as unwrapOrElse,ve as updateEntityById,b as vo,ke as voEquals,Se as voWithValidation,be as voWithValidationUnsafe,re as withCommit,A as withEvent};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/aggregate/aggregate.ts","../src/aggregate/aggregate-base.ts","../src/core/result.ts","../src/aggregate/aggregate-event-sourced.ts","../src/app/command-bus.ts","../src/app/handler.ts","../src/app/query-bus.ts","../src/core/guard.ts","../src/entity/entity.ts","../src/events/event-bus.ts","../src/value-object/value-object.ts"],"names":["aggregate","state","version","__name","withEvent","agg","evt","bump","createDomainEvent","type","payload","options","createDomainEventWithMetadata","metadata","copyMetadata","sourceEvent","additionalMetadata","mergeMetadata","metadataObjects","sameAggregate","a","b","AggregateBase","id","initialState","config","newState","bumpVersion","snapshot","ok","value","err","error","isOk","result","isErr","AggregateEventSourced","_event","event","isNew","validation","handler","history","eventsAfterSnapshot","CommandBus","commandType","command","withCommit","deps","fn","events","QueryBus","queryType","query","guard","cond","sameEntity","findEntityById","entities","entity","hasEntityId","removeEntityById","updateEntityById","updater","replaceEntityById","replacement","entityIds","EventBusImpl","eventType","handlersForType","deepFreeze","obj","visited","propNames","name","vo","voEquals","voWithValidation","validate","errorMessage","voWithValidationUnsafe"],"mappings":"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","file":"index.js","sourcesContent":["import type { Id } from \"../core/id\";\n\nexport type Version = number & { readonly __v: true };\n\n/**\n * Metadata associated with a domain event for traceability and correlation.\n * Used in event-driven architectures to track event flow across services.\n */\nexport interface EventMetadata {\n\t/**\n\t * Correlation ID for tracing events across multiple services/components.\n\t * Typically used to group related events in a distributed system.\n\t */\n\tcorrelationId?: string;\n\n\t/**\n\t * Causation ID referencing the event or command that caused this event.\n\t * Used to build event chains and understand causality.\n\t */\n\tcausationId?: string;\n\n\t/**\n\t * User ID of the person or system that triggered the event.\n\t */\n\tuserId?: string;\n\n\t/**\n\t * Source service or component that produced the event.\n\t */\n\tsource?: string;\n\n\t/**\n\t * Additional custom metadata fields.\n\t * Allows extensibility for domain-specific metadata.\n\t */\n\t[key: string]: unknown;\n}\n\n/**\n * Domain Event represents something meaningful that happened in the domain.\n * Events are immutable and carry information about what occurred.\n *\n * @template T - The event type name (e.g., \"OrderCreated\")\n * @template P - The event payload type\n */\nexport interface DomainEvent<T extends string, P> {\n\t/**\n\t * The type of the event, used for routing and handling.\n\t */\n\ttype: T;\n\n\t/**\n\t * The event payload containing the domain data.\n\t */\n\tpayload: P;\n\n\t/**\n\t * Timestamp when the event occurred.\n\t */\n\toccurredAt: Date;\n\n\t/**\n\t * Event schema version for handling schema evolution.\n\t * Defaults to 1 if not specified. Higher versions indicate schema changes.\n\t */\n\tversion?: number;\n\n\t/**\n\t * Optional metadata for traceability, correlation, and auditing.\n\t * Includes correlationId, causationId, userId, source, and custom fields.\n\t */\n\tmetadata?: EventMetadata;\n}\n\nexport interface Aggregate<State, Evt extends DomainEvent<string, unknown>> {\n\tstate: Readonly<State>;\n\tversion: Version;\n\tpendingEvents: ReadonlyArray<Evt>;\n}\n\nexport function aggregate<State, Evt extends DomainEvent<string, unknown>>(\n\tstate: State,\n\tversion: Version = 0 as Version,\n): Aggregate<State, Evt> {\n\treturn { state, version, pendingEvents: [] };\n}\n\nexport function withEvent<S, E extends DomainEvent<string, unknown>>(\n\tagg: Aggregate<S, E>,\n\tevt: E,\n): Aggregate<S, E> {\n\treturn { ...agg, pendingEvents: [...agg.pendingEvents, evt] };\n}\n\nexport function bump<S, E extends DomainEvent<string, unknown>>(\n\tagg: Aggregate<S, E>,\n): Aggregate<S, E> {\n\treturn { ...agg, version: (agg.version + 1) as Version };\n}\n\n/**\n * Creates a domain event with default values.\n * Sets occurredAt to current date and version to 1 if not provided.\n *\n * @param type - The event type\n * @param payload - The event payload\n * @param options - Optional event configuration\n * @returns A domain event\n *\n * @example\n * ```typescript\n * const event = createDomainEvent(\"OrderCreated\", { orderId: \"123\" });\n * ```\n */\nexport function createDomainEvent<T extends string, P>(\n\ttype: T,\n\tpayload: P,\n\toptions?: {\n\t\toccurredAt?: Date;\n\t\tversion?: number;\n\t\tmetadata?: EventMetadata;\n\t},\n): DomainEvent<T, P> {\n\treturn {\n\t\ttype,\n\t\tpayload,\n\t\toccurredAt: options?.occurredAt ?? new Date(),\n\t\tversion: options?.version ?? 1,\n\t\tmetadata: options?.metadata,\n\t};\n}\n\n/**\n * Creates a domain event with metadata for traceability.\n * Convenience function for creating events with correlation and causation IDs.\n *\n * @param type - The event type\n * @param payload - The event payload\n * @param metadata - Event metadata for traceability\n * @param options - Optional event configuration\n * @returns A domain event with metadata\n *\n * @example\n * ```typescript\n * const event = createDomainEventWithMetadata(\n * \"OrderCreated\",\n * { orderId: \"123\" },\n * {\n * correlationId: \"corr-123\",\n * causationId: \"cmd-456\",\n * userId: \"user-789\"\n * }\n * );\n * ```\n */\nexport function createDomainEventWithMetadata<T extends string, P>(\n\ttype: T,\n\tpayload: P,\n\tmetadata: EventMetadata,\n\toptions?: {\n\t\toccurredAt?: Date;\n\t\tversion?: number;\n\t},\n): DomainEvent<T, P> {\n\treturn createDomainEvent(type, payload, {\n\t\t...options,\n\t\tmetadata,\n\t});\n}\n\n/**\n * Copies metadata from a source event to a new event.\n * Useful for maintaining correlation chains in event-driven architectures.\n *\n * @param sourceEvent - The source event to copy metadata from\n * @param additionalMetadata - Additional metadata to merge in\n * @returns Event metadata with copied and merged values\n *\n * @example\n * ```typescript\n * const newEvent = createDomainEvent(\n * \"OrderShipped\",\n * { orderId: \"123\" },\n * {\n * metadata: copyMetadata(previousEvent, { causationId: previousEvent.type })\n * }\n * );\n * ```\n */\nexport function copyMetadata(\n\tsourceEvent: DomainEvent<string, unknown>,\n\tadditionalMetadata?: Partial<EventMetadata>,\n): EventMetadata {\n\treturn {\n\t\t...(sourceEvent.metadata ?? {}),\n\t\t...(additionalMetadata ?? {}),\n\t};\n}\n\n/**\n * Merges multiple metadata objects into one.\n * Later metadata objects override earlier ones for the same keys.\n *\n * @param metadataObjects - Array of metadata objects to merge\n * @returns Merged event metadata\n *\n * @example\n * ```typescript\n * const metadata = mergeMetadata(\n * { correlationId: \"corr-123\" },\n * { userId: \"user-456\" },\n * { source: \"order-service\" }\n * );\n * ```\n */\nexport function mergeMetadata(\n\t...metadataObjects: Array<EventMetadata | undefined>\n): EventMetadata {\n\treturn Object.assign({}, ...metadataObjects.filter(Boolean));\n}\n\n/**\n * Snapshot of an aggregate state at a specific point in time.\n * Used for optimizing event replay by starting from a snapshot\n * instead of replaying all events from the beginning.\n *\n * @template TState - The type of the aggregate state\n */\nexport interface AggregateSnapshot<TState> {\n\t/**\n\t * The state of the aggregate at the time of the snapshot.\n\t */\n\tstate: TState;\n\n\t/**\n\t * The version of the aggregate when the snapshot was taken.\n\t */\n\tversion: Version;\n\n\t/**\n\t * Timestamp when the snapshot was created.\n\t */\n\tsnapshotAt: Date;\n}\n\n/**\n * Checks if two aggregates are the same (same ID and version).\n * Useful for optimistic concurrency control checks.\n *\n * @param a - First aggregate\n * @param b - Second aggregate\n * @returns true if both aggregates have the same ID and version\n *\n * @example\n * ```typescript\n * const aggregate1 = await repository.getById(id);\n * // ... some operations ...\n * const aggregate2 = await repository.getById(id);\n *\n * if (!sameAggregate(aggregate1, aggregate2)) {\n * throw new Error(\"Aggregate was modified by another process\");\n * }\n * ```\n */\nexport function sameAggregate<TId extends Id<string>>(\n\ta: { id: TId; version: Version },\n\tb: { id: TId; version: Version },\n): boolean {\n\treturn a.id === b.id && a.version === b.version;\n}\n","import type { Id } from \"../core/id\";\nimport type { AggregateSnapshot, Version } from \"./aggregate\";\n\n/**\n * Configuration options for AggregateBase behavior.\n */\nexport interface AggregateConfig {\n\t/**\n\t * Whether to automatically bump the version when state changes.\n\t * Defaults to false. Set to true for automatic versioning.\n\t */\n\tautoVersionBump?: boolean;\n}\n\n/**\n * Base class for Aggregates without Event Sourcing.\n * Provides core functionality for aggregates:\n * - ID and Version management (for Optimistic Concurrency Control)\n * - State management\n * - Snapshot support for performance optimization\n *\n * Use this class when you don't need Event Sourcing but still want\n * aggregate patterns with versioning and state management.\n *\n * @template TState - The type of the aggregate state\n * @template TId - The type of the aggregate identifier\n *\n * @example\n * ```typescript\n * class Order extends AggregateBase<OrderState, OrderId> {\n * constructor(id: OrderId, initialState: OrderState) {\n * super(id, initialState);\n * }\n *\n * confirm(): void {\n * this._state = { ...this._state, status: \"confirmed\" };\n * this.bumpVersion();\n * }\n * }\n * ```\n */\nexport abstract class AggregateBase<TState, TId extends Id<string>> {\n\tpublic readonly id: TId;\n\tpublic version: Version = 0 as Version;\n\n\tprivate readonly _config: AggregateConfig;\n\tprivate readonly _autoVersionBump: boolean;\n\n\tpublic get state(): TState {\n\t\treturn this._state;\n\t}\n\n\t/**\n\t * The state is 'protected' so that only the subclass can change it.\n\t * Subclasses can mutate this directly or use helper methods.\n\t */\n\tprotected _state: TState;\n\n\tprotected constructor(\n\t\tid: TId,\n\t\tinitialState: TState,\n\t\tconfig?: AggregateConfig,\n\t) {\n\t\tthis.id = id;\n\t\tthis._state = initialState;\n\t\tthis._config = config ?? {};\n\t\tthis._autoVersionBump = this._config.autoVersionBump ?? false;\n\t}\n\n\t/**\n\t * Manually bumps the aggregate version.\n\t * Call this after state changes for Optimistic Concurrency Control.\n\t *\n\t * If `autoVersionBump` is enabled, this is called automatically\n\t * when using `setState()`.\n\t */\n\tprotected bumpVersion(): void {\n\t\tthis.version = (this.version + 1) as Version;\n\t}\n\n\t/**\n\t * Sets the state and optionally bumps the version automatically.\n\t * This is a convenience method for state mutations.\n\t *\n\t * @param newState - The new state\n\t * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)\n\t */\n\tprotected setState(\n\t\tnewState: TState,\n\t\tbumpVersion?: boolean,\n\t): void {\n\t\tthis._state = newState;\n\t\tconst shouldBump = bumpVersion ?? this._autoVersionBump;\n\t\tif (shouldBump) {\n\t\t\tthis.bumpVersion();\n\t\t}\n\t}\n\n\t/**\n\t * Creates a snapshot of the current aggregate state.\n\t * Useful for performance optimization, backup/restore, and audit trails.\n\t *\n\t * @returns A snapshot containing the current state and version\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = aggregate.createSnapshot();\n\t * await snapshotRepository.save(aggregate.id, snapshot);\n\t * ```\n\t */\n\tpublic createSnapshot(): AggregateSnapshot<TState> {\n\t\treturn {\n\t\t\tstate: { ...this._state } as TState,\n\t\t\tversion: this.version,\n\t\t\tsnapshotAt: new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Restores the aggregate from a snapshot.\n\t * This is useful for loading aggregates from snapshots instead of\n\t * rebuilding them from scratch.\n\t *\n\t * @param snapshot - The snapshot to restore from\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = await snapshotRepository.getLatest(aggregateId);\n\t * aggregate.restoreFromSnapshot(snapshot);\n\t * ```\n\t */\n\tpublic restoreFromSnapshot(snapshot: AggregateSnapshot<TState>): void {\n\t\tthis._state = snapshot.state;\n\t\tthis.version = snapshot.version;\n\t}\n}\n","export type Ok<T> = { ok: true; value: T };\nexport type Err<E> = { ok: false; error: E };\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/**\n * Creates an Ok result with a value.\n *\n * @param value - The success value\n * @returns An Ok result containing the value\n *\n * @example\n * ```typescript\n * const result = ok(42);\n * // result is Ok<number>\n * ```\n */\nexport function ok<T>(value: T): Ok<T>;\n\n/**\n * Creates an Ok result with void (no value).\n *\n * @returns An Ok<void> result\n *\n * @example\n * ```typescript\n * const result = ok();\n * // result is Ok<void>\n * ```\n */\nexport function ok(): Ok<void>;\n\nexport function ok<T>(value?: T): Ok<T> {\n\treturn { ok: true, value: value as T };\n}\n\n/**\n * Creates an Err result with an error.\n *\n * @param error - The error value\n * @returns An Err result containing the error\n *\n * @example\n * ```typescript\n * const result = err(\"Something went wrong\");\n * // result is Err<string>\n * ```\n */\nexport function err<E>(error: E): Err<E>;\n\n/**\n * Creates an Err result with void (no error value).\n *\n * @returns An Err<void> result\n *\n * @example\n * ```typescript\n * const result = err();\n * // result is Err<void>\n * ```\n */\nexport function err(): Err<void>;\n\nexport function err<E>(error?: E): Err<E> {\n\treturn { ok: false, error: error as E };\n}\n\n/**\n * Type guard to check if a Result is Ok.\n * Narrows the type to Ok<T> when returning true.\n *\n * @param result - The result to check\n * @returns true if the result is Ok, false otherwise\n *\n * @example\n * ```typescript\n * const result = voWithValidation(data, validator);\n * if (isOk(result)) {\n * // TypeScript knows result is Ok<ValueObject<T>>\n * console.log(result.value);\n * }\n * ```\n */\nexport function isOk<T, E>(result: Result<T, E>): result is Ok<T> {\n\treturn result.ok === true;\n}\n\n/**\n * Type guard to check if a Result is Err.\n * Narrows the type to Err<E> when returning true.\n *\n * @param result - The result to check\n * @returns true if the result is Err, false otherwise\n *\n * @example\n * ```typescript\n * const result = voWithValidation(data, validator);\n * if (isErr(result)) {\n * // TypeScript knows result is Err<string>\n * console.error(result.error);\n * }\n * ```\n */\nexport function isErr<T, E>(result: Result<T, E>): result is Err<E> {\n\treturn result.ok === false;\n}\n","import { err, ok, type Result } from \"../core/result\";\nimport type { Id } from \"../core/id\";\nimport { AggregateBase, type AggregateConfig } from \"./aggregate-base\";\nimport type {\n\tAggregateSnapshot,\n\tDomainEvent,\n\tVersion,\n} from \"./aggregate\";\n\ntype Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;\n\n/**\n * Extended configuration options for AggregateEventSourced behavior.\n */\nexport interface AggregateEventSourcedConfig extends AggregateConfig {\n\t/**\n\t * Whether to automatically bump the version when applying new events.\n\t * Defaults to true. Set to false to manually control versioning.\n\t */\n\tautoVersionBump?: boolean;\n}\n\n/**\n * Base class for Event-Sourced Aggregates.\n * Extends `AggregateBase` with Event Sourcing capabilities:\n * - Event tracking (pendingEvents)\n * - Event handlers for state transitions\n * - Event validation\n * - History replay\n *\n * Use this class when you want Event Sourcing with full event tracking\n * and replay capabilities.\n *\n * @template TState - The type of the aggregate state\n * @template TEvent - The union type of all domain events\n * @template TId - The type of the aggregate identifier\n *\n * @example\n * ```typescript\n * class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {\n * confirm(): void {\n * this.apply(createDomainEvent(\"OrderConfirmed\", {}));\n * }\n *\n * protected readonly handlers = {\n * OrderConfirmed: (state: OrderState): OrderState => ({\n * ...state,\n * status: \"confirmed\",\n * }),\n * };\n * }\n * ```\n */\nexport abstract class AggregateEventSourced<\n\tTState,\n\tTEvent extends DomainEvent<string, unknown>,\n\tTId extends Id<string>,\n> extends AggregateBase<TState, TId> {\n\tprivate readonly _eventConfig: AggregateEventSourcedConfig;\n\tprivate readonly _eventAutoVersionBump: boolean;\n\n\tprivate readonly _pendingEvents: TEvent[] = [];\n\n\tprotected constructor(\n\t\tid: TId,\n\t\tinitialState: TState,\n\t\tconfig?: AggregateEventSourcedConfig,\n\t) {\n\t\tsuper(id, initialState, config);\n\t\tthis._eventConfig = config ?? {};\n\t\tthis._eventAutoVersionBump = this._eventConfig.autoVersionBump ?? true;\n\t}\n\n\t/**\n\t * Returns a read-only list of new, not-yet-persisted events.\n\t */\n\tpublic get pendingEvents(): ReadonlyArray<TEvent> {\n\t\treturn this._pendingEvents;\n\t}\n\n\t/**\n\t * Clears the list of pending events.\n\t * Typically called after the events have been persisted.\n\t */\n\tpublic clearPendingEvents(): void {\n\t\tthis._pendingEvents.length = 0;\n\t}\n\n\t/**\n\t * Validates an event before it is applied.\n\t * Override this method to add custom validation logic.\n\t * Return `ok(true)` if the event is valid, `err(message)` otherwise.\n\t *\n\t * @param event - The event to validate\n\t * @returns Result indicating if the event is valid\n\t *\n\t * @example\n\t * ```typescript\n\t * protected validateEvent(event: OrderEvent): Result<true, string> {\n\t * if (event.type === \"OrderShipped\" && this.state.status !== \"confirmed\") {\n\t * return err(\"Order must be confirmed before shipping\");\n\t * }\n\t * return ok(true);\n\t * }\n\t * ```\n\t */\n\tprotected validateEvent(_event: TEvent): Result<true, string> {\n\t\treturn ok(true);\n\t}\n\n\t/**\n\t * Applies an event to change the state and adds it\n\t * to the list of pending events.\n\t * Returns a Result type instead of throwing an error.\n\t *\n\t * @param event - The domain event to apply\n\t * @param isNew - Indicates whether the event is new (and needs to be persisted)\n\t * or if it is being loaded from history\n\t * @returns Result<void, string> - ok if successful, err with error message if validation fails or handler is missing\n\t */\n\tprotected apply(event: TEvent, isNew = true): Result<void, string> {\n\t\t// Validate event before applying\n\t\tconst validation = this.validateEvent(event);\n\t\tif (!validation.ok) {\n\t\t\treturn err(\n\t\t\t\t`Event validation failed for ${event.type}: ${validation.error}`,\n\t\t\t);\n\t\t}\n\n\t\tconst handler = this.handlers[event.type as keyof typeof this.handlers];\n\t\tif (!handler) {\n\t\t\treturn err(`Missing handler for event type: ${event.type}`);\n\t\t}\n\n\t\t// First, change the state\n\t\tthis._state = handler(\n\t\t\tthis._state,\n\t\t\tevent as Extract<TEvent, { type: TEvent[\"type\"] }>,\n\t\t);\n\n\t\t// Then (if new) add the event to the list and bump version\n\t\tif (isNew) {\n\t\t\tthis._pendingEvents.push(event);\n\t\t\tif (this._eventAutoVersionBump) {\n\t\t\t\tthis.version = (this.version + 1) as Version;\n\t\t\t}\n\t\t}\n\n\t\treturn ok();\n\t}\n\n\t/**\n\t * Applies an event to change the state and adds it\n\t * to the list of pending events.\n\t * Throws an error if validation fails or handler is missing.\n\t *\n\t * @param event - The domain event to apply\n\t * @param isNew - Indicates whether the event is new (and needs to be persisted)\n\t * or if it is being loaded from history\n\t * @throws Error if event validation fails or handler is missing\n\t */\n\tprotected applyUnsafe(event: TEvent, isNew = true): void {\n\t\t// Validate event before applying\n\t\tconst validation = this.validateEvent(event);\n\t\tif (!validation.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Event validation failed for ${event.type}: ${validation.error}`,\n\t\t\t);\n\t\t}\n\n\t\tconst handler = this.handlers[event.type as keyof typeof this.handlers];\n\t\tif (!handler) {\n\t\t\tthrow new Error(`Missing handler for event type: ${event.type}`);\n\t\t}\n\n\t\t// First, change the state\n\t\tthis._state = handler(\n\t\t\tthis._state,\n\t\t\tevent as Extract<TEvent, { type: TEvent[\"type\"] }>,\n\t\t);\n\n\t\t// Then (if new) add the event to the list and bump version\n\t\tif (isNew) {\n\t\t\tthis._pendingEvents.push(event);\n\t\t\tif (this._eventAutoVersionBump) {\n\t\t\t\tthis.version = (this.version + 1) as Version;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manually bumps the aggregate version.\n\t * Only needed if `autoVersionBump` is disabled.\n\t */\n\tprotected bumpVersion(): void {\n\t\tthis.version = (this.version + 1) as Version;\n\t}\n\n\t/**\n\t * Reconstitutes the aggregate from an event history.\n\t * Sets the version to the number of events in the history.\n\t *\n\t * @param history - An ordered list of past events\n\t */\n\tpublic loadFromHistory(history: TEvent[]): Result<void, string> {\n\t\tfor (const event of history) {\n\t\t\tconst result = this.apply(event, false); // 'false' as it's not a new event\n\t\t\tif (!result.ok) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\t// Set version to the number of events in history\n\t\tthis.version = history.length as Version;\n\t\treturn ok();\n\t}\n\n\t/**\n\t * Checks if the aggregate has any pending events.\n\t *\n\t * @returns true if there are pending events, false otherwise\n\t */\n\tpublic hasPendingEvents(): boolean {\n\t\treturn this._pendingEvents.length > 0;\n\t}\n\n\t/**\n\t * Returns the number of pending events.\n\t *\n\t * @returns The count of pending events\n\t */\n\tpublic getEventCount(): number {\n\t\treturn this._pendingEvents.length;\n\t}\n\n\t/**\n\t * Returns the latest pending event, if any.\n\t *\n\t * @returns The most recent event or undefined if no events exist\n\t */\n\tpublic getLatestEvent(): TEvent | undefined {\n\t\treturn this._pendingEvents[this._pendingEvents.length - 1];\n\t}\n\n\t/**\n\t * Restores the aggregate from a snapshot and applies events that occurred after the snapshot.\n\t * This is more efficient than replaying all events from the beginning.\n\t *\n\t * @param snapshot - The snapshot to restore from\n\t * @param eventsAfterSnapshot - Events that occurred after the snapshot was taken\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = await snapshotRepository.getLatest(aggregateId);\n\t * const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);\n\t * aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);\n\t * ```\n\t */\n\tpublic restoreFromSnapshotWithEvents(\n\t\tsnapshot: AggregateSnapshot<TState>,\n\t\teventsAfterSnapshot: TEvent[],\n\t): Result<void, string> {\n\t\tthis._state = snapshot.state;\n\t\tthis.version = snapshot.version;\n\n\t\t// Apply events that occurred after the snapshot\n\t\tfor (const event of eventsAfterSnapshot) {\n\t\t\tconst result = this.apply(event, false);\n\t\t\tif (!result.ok) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\t// Set version to snapshot version + events after snapshot\n\t\tthis.version = (snapshot.version + eventsAfterSnapshot.length) as Version;\n\t\treturn ok();\n\t}\n\n\t/**\n\t * A map of event types to their corresponding handlers.\n\t * Subclasses MUST implement this property.\n\t */\n\tprotected abstract readonly handlers: {\n\t\t[K in TEvent[\"type\"]]: Handler<TState, Extract<TEvent, { type: K }>>;\n\t};\n}\n\n","import type { Result } from \"../core/result\";\nimport { err } from \"../core/result\";\nimport type { Command, CommandHandler } from \"./command\";\n\n/**\n * Command Bus interface for dispatching commands to their handlers.\n * Provides a centralized way to execute commands with handler registration.\n *\n * @example\n * ```typescript\n * const bus = new CommandBus();\n * bus.register(\"CreateOrder\", createOrderHandler);\n *\n * const result = await bus.execute({\n * type: \"CreateOrder\",\n * customerId: \"123\",\n * items: [...]\n * });\n * ```\n */\nexport interface ICommandBus {\n\t/**\n\t * Executes a command by dispatching it to the registered handler.\n\t *\n\t * @param command - The command to execute\n\t * @returns Result containing the success value or error message\n\t */\n\texecute<C extends Command, R>(command: C): Promise<Result<R, string>>;\n\n\t/**\n\t * Registers a handler for a specific command type.\n\t *\n\t * @param commandType - The command type to register the handler for\n\t * @param handler - The handler function for this command type\n\t */\n\tregister<C extends Command, R>(\n\t\tcommandType: C[\"type\"],\n\t\thandler: CommandHandler<C, R>,\n\t): void;\n}\n\n/**\n * Simple in-memory command bus implementation.\n * Handlers are stored in a Map and dispatched based on command type.\n *\n * **Note:** This is a basic implementation suitable for development and simple use cases.\n * For production environments, consider implementing or using a more feature-rich bus that includes:\n * - Middleware/Pipeline support (logging, validation, authorization)\n * - Error handling and retry logic\n * - Timeout handling\n * - Metrics and observability\n * - Transaction management\n * - Dead letter queue support\n *\n * The `CommandHandler` type can still be used with external production-grade buses\n * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.\n *\n * @example\n * ```typescript\n * const bus = new CommandBus();\n * bus.register(\"CreateOrder\", async (cmd) => {\n * // ... handler logic\n * return ok(orderId);\n * });\n *\n * const result = await bus.execute({ type: \"CreateOrder\", ... });\n * ```\n */\nexport class CommandBus implements ICommandBus {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, CommandHandler<any, any>>();\n\n\tregister<C extends Command, R>(\n\t\tcommandType: C[\"type\"],\n\t\thandler: CommandHandler<C, R>,\n\t): void {\n\t\tthis.handlers.set(commandType, handler);\n\t}\n\n\tasync execute<C extends Command, R>(\n\t\tcommand: C,\n\t): Promise<Result<R, string>> {\n\t\tconst handler = this.handlers.get(command.type);\n\t\tif (!handler) {\n\t\t\treturn err(`No handler registered for command type: ${command.type}`);\n\t\t}\n\t\treturn handler(command);\n\t}\n}\n\n","import type { EventBus, Outbox } from \"../events/ports\";\nimport type { UnitOfWork } from \"../repo/uow\";\n\n/**\n * Helper function for executing commands within a transaction.\n * Handles event persistence via outbox and optional event bus publishing.\n *\n * @param deps - Dependencies including outbox, optional event bus, and unit of work\n * @param fn - Function that returns result and events\n * @returns The result wrapped in a transaction\n *\n * @example\n * ```typescript\n * const result = await withCommit(\n * { outbox, bus, uow },\n * async () => {\n * const order = Order.create(customerId, items);\n * await repository.save(order);\n * return {\n * result: order.id,\n * events: order.pendingEvents\n * };\n * }\n * );\n * ```\n */\nexport function withCommit<Evt, R>(\n\tdeps: {\n\t\toutbox: Outbox<Evt>;\n\t\tbus?: EventBus<Evt>;\n\t\tuow: UnitOfWork;\n\t},\n\tfn: () => Promise<{ result: R; events: ReadonlyArray<Evt> }>,\n) {\n\treturn deps.uow.transactional(async () => {\n\t\tconst { result, events } = await fn();\n\t\tawait deps.outbox.add(events);\n\t\tif (deps.bus) await deps.bus.publish(events);\n\t\treturn result;\n\t});\n}\n","import { err, ok, type Result } from \"../core/result\";\nimport type { Query, QueryHandler } from \"./query\";\n\n/**\n * Query Bus interface for dispatching queries to their handlers.\n * Provides a centralized way to execute queries with handler registration.\n *\n * @example\n * ```typescript\n * const bus = new QueryBus();\n * bus.register(\"GetOrder\", getOrderHandler);\n *\n * const order = await bus.execute({\n * type: \"GetOrder\",\n * orderId: \"123\"\n * });\n * ```\n */\nexport interface IQueryBus {\n\t/**\n\t * Executes a query by dispatching it to the registered handler.\n\t * Returns a Result type instead of throwing an error.\n\t *\n\t * @param query - The query to execute\n\t * @returns Result containing the query result if successful, or an error message if no handler is registered\n\t */\n\texecute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;\n\n\t/**\n\t * Executes a query by dispatching it to the registered handler.\n\t * Throws an error if no handler is registered.\n\t *\n\t * @param query - The query to execute\n\t * @returns The query result\n\t * @throws Error if no handler is registered for the query type\n\t */\n\texecuteUnsafe<Q extends Query, R>(query: Q): Promise<R>;\n\n\t/**\n\t * Registers a handler for a specific query type.\n\t *\n\t * @param queryType - The query type to register the handler for\n\t * @param handler - The handler function for this query type\n\t */\n\tregister<Q extends Query, R>(\n\t\tqueryType: Q[\"type\"],\n\t\thandler: QueryHandler<Q, R>,\n\t): void;\n}\n\n/**\n * Simple in-memory query bus implementation.\n * Handlers are stored in a Map and dispatched based on query type.\n *\n * **Note:** This is a basic implementation suitable for development and simple use cases.\n * For production environments, consider implementing or using a more feature-rich bus that includes:\n * - Middleware/Pipeline support (logging, caching, rate limiting)\n * - Error handling\n * - Timeout handling\n * - Metrics and observability\n * - Query result caching\n * - Rate limiting\n *\n * The `QueryHandler` type can still be used with external production-grade buses\n * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.\n *\n * @example\n * ```typescript\n * const bus = new QueryBus();\n * bus.register(\"GetOrder\", async (query) => {\n * return await repository.getById(query.orderId);\n * });\n *\n * const order = await bus.execute({ type: \"GetOrder\", orderId: \"123\" });\n * ```\n */\nexport class QueryBus implements IQueryBus {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, QueryHandler<any, any>>();\n\n\tregister<Q extends Query, R>(\n\t\tqueryType: Q[\"type\"],\n\t\thandler: QueryHandler<Q, R>,\n\t): void {\n\t\tthis.handlers.set(queryType, handler);\n\t}\n\n\tasync execute<Q extends Query, R>(query: Q): Promise<Result<R, string>> {\n\t\tconst handler = this.handlers.get(query.type);\n\t\tif (!handler) {\n\t\t\treturn err(`No handler registered for query type: ${query.type}`);\n\t\t}\n\t\ttry {\n\t\t\tconst result = await handler(query);\n\t\t\treturn ok(result);\n\t\t} catch (error) {\n\t\t\treturn err(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t);\n\t\t}\n\t}\n\n\tasync executeUnsafe<Q extends Query, R>(query: Q): Promise<R> {\n\t\tconst handler = this.handlers.get(query.type);\n\t\tif (!handler) {\n\t\t\tthrow new Error(`No handler registered for query type: ${query.type}`);\n\t\t}\n\t\treturn handler(query);\n\t}\n}\n\n","import { err, ok, type Result } from \"./result\";\n\n/**\n * Guard function that validates a condition and returns a Result.\n * Returns `ok(true)` if the condition is met, otherwise `err(error)`.\n *\n * @param cond - The condition to check\n * @param error - Error message if condition fails\n * @returns Result<true, string>\n *\n * @example\n * ```typescript\n * const result = guard(id.length > 0, \"ID cannot be empty\");\n * if (!result.ok) {\n * return err(result.error);\n * }\n * ```\n */\nexport function guard(cond: boolean, error: string): Result<true, string> {\n\treturn cond ? ok(true) : err(error);\n}\n","/**\n * Optional interface for entities with identity.\n * Use this when you need explicit entity types for nested entities\n * within aggregates or for entities that are not aggregate roots.\n *\n * @template TId - The type of the entity identifier\n *\n * @example\n * ```typescript\n * type OrderItem = Entity<ItemId> & {\n * productId: string;\n * quantity: number;\n * };\n * ```\n */\nexport interface Entity<TId> {\n\treadonly id: TId;\n}\n\n/**\n * Checks if two entities have the same ID.\n * Works with any object that has an 'id' property.\n *\n * @param a - First entity\n * @param b - Second entity\n * @returns true if both entities have the same ID, false otherwise\n *\n * @example\n * ```typescript\n * const item1: OrderItem = { id: itemId1, productId: \"prod-1\", quantity: 2 };\n * const item2: OrderItem = { id: itemId2, productId: \"prod-2\", quantity: 1 };\n *\n * sameEntity(item1, item2); // false\n * sameEntity(item1, item1); // true\n * ```\n */\nexport function sameEntity<TId>(a: { id: TId }, b: { id: TId }): boolean {\n\treturn a.id === b.id;\n}\n\n/**\n * Finds an entity by ID in a collection.\n * Returns undefined if not found.\n *\n * @param entities - Array of entities to search\n * @param id - The ID to search for\n * @returns The entity if found, undefined otherwise\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const item = findEntityById(items, itemId1);\n * // item is { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ```\n */\nexport function findEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): T | undefined {\n\treturn entities.find((entity) => entity.id === id);\n}\n\n/**\n * Checks if an entity with the given ID exists in the collection.\n *\n * @param entities - Array of entities to search\n * @param id - The ID to check for\n * @returns true if an entity with the ID exists, false otherwise\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * hasEntityId(items, itemId1); // true\n * hasEntityId(items, itemId2); // false\n * ```\n */\nexport function hasEntityId<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): boolean {\n\treturn entities.some((entity) => entity.id === id);\n}\n\n/**\n * Removes an entity with the given ID from the collection.\n * Returns a new array without the entity.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to remove\n * @returns A new array without the entity with the given ID\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const updated = removeEntityById(items, itemId1);\n * // updated is [{ id: itemId2, productId: \"prod-2\", quantity: 1 }]\n * ```\n */\nexport function removeEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): T[] {\n\treturn entities.filter((entity) => entity.id !== id);\n}\n\n/**\n * Updates an entity with the given ID in the collection.\n * Returns a new array with the updated entity.\n * If the entity is not found, returns the original array unchanged.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to update\n * @param updater - Function that takes the entity and returns the updated entity\n * @returns A new array with the updated entity\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * const updated = updateEntityById(items, itemId1, (item) => ({\n * ...item,\n * quantity: item.quantity + 1\n * }));\n * // updated is [{ id: itemId1, productId: \"prod-1\", quantity: 3 }]\n * ```\n */\nexport function updateEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n\tupdater: (entity: T) => T,\n): T[] {\n\treturn entities.map((entity) => (entity.id === id ? updater(entity) : entity));\n}\n\n/**\n * Replaces an entity with the given ID in the collection.\n * Returns a new array with the replaced entity.\n * If the entity is not found, returns the original array unchanged.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to replace\n * @param replacement - The replacement entity\n * @returns A new array with the replaced entity\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * const updated = replaceEntityById(items, itemId1, {\n * id: itemId1,\n * productId: \"prod-1\",\n * quantity: 5\n * });\n * ```\n */\nexport function replaceEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n\treplacement: T,\n): T[] {\n\treturn entities.map((entity) => (entity.id === id ? replacement : entity));\n}\n\n/**\n * Extracts all IDs from a collection of entities.\n *\n * @param entities - Array of entities\n * @returns Array of entity IDs\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const ids = entityIds(items);\n * // ids is [itemId1, itemId2]\n * ```\n */\nexport function entityIds<TId, T extends { id: TId }>(entities: T[]): TId[] {\n\treturn entities.map((entity) => entity.id);\n}\n\n","import type { DomainEvent } from \"../aggregate/aggregate\";\nimport type { EventBus, EventHandler } from \"./ports\";\n\n/**\n * Simple in-memory event bus implementation.\n * Supports multiple subscribers per event type (pub/sub pattern).\n *\n * @template Evt - The type of domain events (must extend DomainEvent)\n *\n * @example\n * ```typescript\n * const bus = new EventBusImpl<OrderEvent>();\n *\n * bus.subscribe(\"OrderCreated\", async (event) => {\n * await sendEmail(event.payload.customerId);\n * });\n *\n * bus.subscribe(\"OrderCreated\", async (event) => {\n * await logEvent(event);\n * });\n *\n * await bus.publish([orderCreatedEvent]);\n * // Both handlers will be called\n * ```\n */\nexport class EventBusImpl<Evt extends DomainEvent<string, unknown>>\n\timplements EventBus<Evt>\n{\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, Set<EventHandler<any>>>();\n\n\tsubscribe<T extends Evt>(\n\t\teventType: string,\n\t\thandler: EventHandler<T>,\n\t): () => void {\n\t\tconst type = eventType;\n\t\tif (!this.handlers.has(type)) {\n\t\t\tthis.handlers.set(type, new Set());\n\t\t}\n\t\tconst handlersForType = this.handlers.get(type)!;\n\t\thandlersForType.add(handler);\n\n\t\t// Return unsubscribe function\n\t\treturn () => {\n\t\t\thandlersForType.delete(handler);\n\t\t\tif (handlersForType.size === 0) {\n\t\t\t\tthis.handlers.delete(type);\n\t\t\t}\n\t\t};\n\t}\n\n\tasync publish(events: ReadonlyArray<Evt>): Promise<void> {\n\t\tfor (const event of events) {\n\t\t\tconst handlersForType = this.handlers.get(event.type);\n\t\t\tif (handlersForType) {\n\t\t\t\t// Call all handlers for this event type\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tArray.from(handlersForType).map((handler) => handler(event)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n","import { err, ok, type Result } from \"../core/result\";\n\nexport type ValueObject<T> = Readonly<T>;\n\n/**\n * Deep freezes an object and all its nested properties recursively.\n * This ensures true immutability for value objects with nested structures.\n * Handles circular references by tracking visited objects.\n */\nfunction deepFreeze<T>(obj: T, visited = new WeakSet<object>()): Readonly<T> {\n\t// Handle null and non-objects\n\tif (obj === null || typeof obj !== \"object\") {\n\t\treturn obj as Readonly<T>;\n\t}\n\n\t// Handle circular references\n\tif (visited.has(obj as object)) {\n\t\treturn obj as Readonly<T>;\n\t}\n\n\t// Mark as visited\n\tvisited.add(obj as object);\n\n\t// Retrieve the property names defined on obj\n\tconst propNames = Object.getOwnPropertyNames(obj);\n\n\t// Freeze properties before freezing self\n\tfor (const name of propNames) {\n\t\tconst value = (obj as Record<string, unknown>)[name];\n\n\t\t// Freeze value if it is an object or array\n\t\tif (value && (typeof value === \"object\" || Array.isArray(value))) {\n\t\t\tdeepFreeze(value, visited);\n\t\t}\n\t}\n\n\treturn Object.freeze(obj) as Readonly<T>;\n}\n\n/**\n * Creates a deeply immutable value object from the given data.\n * All nested objects and arrays are frozen recursively.\n *\n * @param t - The data to convert into a value object\n * @returns A deeply frozen, immutable value object\n *\n * @example\n * ```typescript\n * const address = vo({\n * street: \"Main St\",\n * city: \"Berlin\",\n * coordinates: { lat: 52.5, lng: 13.4 }\n * });\n * // address.coordinates.lat = 99; // ❌ Error: Cannot assign to read-only property\n * ```\n */\nexport function vo<T>(t: T): ValueObject<T> {\n\treturn deepFreeze({ ...t });\n}\n\n/**\n * Compares two value objects for equality based on their values.\n * Uses deep equality comparison by serializing both objects to JSON.\n *\n * Note: This is a simple implementation. For production use with complex objects,\n * consider using a more robust deep equality library or implementing custom equality logic.\n *\n * @param a - First value object\n * @param b - Second value object\n * @returns true if both objects have the same values, false otherwise\n *\n * @example\n * ```typescript\n * const money1 = vo({ amount: 100, currency: \"USD\" });\n * const money2 = vo({ amount: 100, currency: \"USD\" });\n * voEquals(money1, money2); // true\n * ```\n */\nexport function voEquals<T>(a: ValueObject<T>, b: ValueObject<T>): boolean {\n\treturn JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * Creates a value object with optional validation.\n * Returns a Result type instead of throwing an error.\n *\n * @param t - The data to convert into a value object\n * @param validate - Validation function that returns true if valid\n * @param errorMessage - Optional custom error message if validation fails\n * @returns Result containing the value object if valid, or an error message if validation fails\n *\n * @example\n * ```typescript\n * const result = voWithValidation(\n * { amount: 100, currency: \"USD\" },\n * (m) => m.amount >= 0 && m.currency.length === 3,\n * \"Invalid money: amount must be non-negative and currency must be 3 characters\"\n * );\n *\n * if (result.ok) {\n * console.log(result.value); // Use the value object\n * } else {\n * console.error(result.error); // Handle validation error\n * }\n * ```\n */\nexport function voWithValidation<T>(\n\tt: T,\n\tvalidate: (value: T) => boolean,\n\terrorMessage?: string,\n): Result<ValueObject<T>, string> {\n\tif (!validate(t)) {\n\t\treturn err(\n\t\t\terrorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`,\n\t\t);\n\t}\n\treturn ok(vo(t));\n}\n\n/**\n * Creates a value object with optional validation.\n * Throws an error if validation fails.\n *\n * @param t - The data to convert into a value object\n * @param validate - Validation function that returns true if valid\n * @param errorMessage - Optional custom error message if validation fails\n * @returns A deeply frozen, immutable value object\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * const money = voWithValidationUnsafe(\n * { amount: 100, currency: \"USD\" },\n * (m) => m.amount >= 0 && m.currency.length === 3,\n * \"Invalid money: amount must be non-negative and currency must be 3 characters\"\n * );\n * ```\n */\nexport function voWithValidationUnsafe<T>(\n\tt: T,\n\tvalidate: (value: T) => boolean,\n\terrorMessage?: string,\n): ValueObject<T> {\n\tif (!validate(t)) {\n\t\tthrow new Error(\n\t\t\terrorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`,\n\t\t);\n\t}\n\treturn vo(t);\n}\n"]}
1
+ {"version":3,"sources":["../src/aggregate/aggregate.ts","../src/aggregate/aggregate-base.ts","../src/core/result/result.ts","../src/core/result/outcome.ts","../src/aggregate/aggregate-event-sourced.ts","../src/app/command-bus.ts","../src/app/handler.ts","../src/app/query-bus.ts","../src/core/guard.ts","../src/entity/entity.ts","../src/events/event-bus.ts","../src/value-object/value-object.ts"],"names":["aggregate","state","version","__name","withEvent","agg","evt","bump","createDomainEvent","type","payload","options","createDomainEventWithMetadata","metadata","copyMetadata","sourceEvent","additionalMetadata","mergeMetadata","metadataObjects","sameAggregate","a","b","AggregateBase","id","initialState","config","newState","bumpVersion","snapshot","ok","value","err","error","isOk","result","isErr","andThen","fn","map","mapErr","unwrapOr","defaultValue","unwrapOrElse","match","onOk","onErr","ResultBase","Success","_Success","Erroneous","_fn","_Erroneous","Outcome","_Outcome","AggregateEventSourced","_event","event","isNew","validation","handler","history","eventsAfterSnapshot","CommandBus","commandType","command","withCommit","deps","events","QueryBus","queryType","query","guard","cond","sameEntity","findEntityById","entities","entity","hasEntityId","removeEntityById","updateEntityById","updater","replaceEntityById","replacement","entityIds","EventBusImpl","eventType","handlersForType","deepFreeze","obj","visited","propNames","name","vo","voEquals","voWithValidation","validate","errorMessage","voWithValidationUnsafe"],"mappings":"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","file":"index.js","sourcesContent":["import type { Id } from \"../core/id\";\n\nexport type Version = number & { readonly __v: true };\n\n/**\n * Metadata associated with a domain event for traceability and correlation.\n * Used in event-driven architectures to track event flow across services.\n */\nexport interface EventMetadata {\n\t/**\n\t * Correlation ID for tracing events across multiple services/components.\n\t * Typically used to group related events in a distributed system.\n\t */\n\tcorrelationId?: string;\n\n\t/**\n\t * Causation ID referencing the event or command that caused this event.\n\t * Used to build event chains and understand causality.\n\t */\n\tcausationId?: string;\n\n\t/**\n\t * User ID of the person or system that triggered the event.\n\t */\n\tuserId?: string;\n\n\t/**\n\t * Source service or component that produced the event.\n\t */\n\tsource?: string;\n\n\t/**\n\t * Additional custom metadata fields.\n\t * Allows extensibility for domain-specific metadata.\n\t */\n\t[key: string]: unknown;\n}\n\n/**\n * Domain Event represents something meaningful that happened in the domain.\n * Events are immutable and carry information about what occurred.\n *\n * @template T - The event type name (e.g., \"OrderCreated\")\n * @template P - The event payload type\n */\nexport interface DomainEvent<T extends string, P> {\n\t/**\n\t * The type of the event, used for routing and handling.\n\t */\n\ttype: T;\n\n\t/**\n\t * The event payload containing the domain data.\n\t */\n\tpayload: P;\n\n\t/**\n\t * Timestamp when the event occurred.\n\t */\n\toccurredAt: Date;\n\n\t/**\n\t * Event schema version for handling schema evolution.\n\t * Defaults to 1 if not specified. Higher versions indicate schema changes.\n\t */\n\tversion?: number;\n\n\t/**\n\t * Optional metadata for traceability, correlation, and auditing.\n\t * Includes correlationId, causationId, userId, source, and custom fields.\n\t */\n\tmetadata?: EventMetadata;\n}\n\n/**\n * Marker interface for Aggregate Roots.\n * \n * In Domain-Driven Design, an Aggregate Root is an Entity (the parent Entity of the aggregate).\n * It represents the aggregate externally and is the only object that external code\n * is allowed to hold references to. All access to child entities within the aggregate\n * must go through the Aggregate Root.\n * \n * An Aggregate consists of:\n * - One Aggregate Root (Entity with id + version)\n * - Optional child entities (Entities with id, but no own version)\n * - Optional value objects\n * \n * The Aggregate Root has identity (id) and version for optimistic concurrency control.\n * Child entities exist only within the aggregate boundary and are versioned through\n * the Aggregate Root.\n *\n * @template TId - The type of the aggregate root identifier\n *\n * @example\n * ```typescript\n * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {\n * // Order is an Aggregate Root (an Entity)\n * // OrderState contains child entities (e.g., OrderItem) and value objects\n * }\n * ```\n */\nexport interface AggregateRoot<TId extends Id<string>> {\n\t/**\n\t * Unique identifier of the aggregate root entity.\n\t */\n\treadonly id: TId;\n\n\t/**\n\t * Version number for optimistic concurrency control.\n\t * Incremented on each state change to detect concurrent modifications.\n\t * This version applies to the entire aggregate, including all child entities.\n\t */\n\treadonly version: Version;\n}\n\n/**\n * Structural interface representing an aggregate with state and events.\n * Used for type constraints in repositories and other infrastructure code.\n *\n * @template State - The type of the aggregate state\n * @template Evt - The union type of all domain events\n */\nexport interface Aggregate<State, Evt extends DomainEvent<string, unknown>> {\n\tstate: Readonly<State>;\n\tversion: Version;\n\tpendingEvents: ReadonlyArray<Evt>;\n}\n\nexport function aggregate<State, Evt extends DomainEvent<string, unknown>>(\n\tstate: State,\n\tversion: Version = 0 as Version,\n): Aggregate<State, Evt> {\n\treturn { state, version, pendingEvents: [] };\n}\n\nexport function withEvent<S, E extends DomainEvent<string, unknown>>(\n\tagg: Aggregate<S, E>,\n\tevt: E,\n): Aggregate<S, E> {\n\treturn { ...agg, pendingEvents: [...agg.pendingEvents, evt] };\n}\n\nexport function bump<S, E extends DomainEvent<string, unknown>>(\n\tagg: Aggregate<S, E>,\n): Aggregate<S, E> {\n\treturn { ...agg, version: (agg.version + 1) as Version };\n}\n\n/**\n * Creates a domain event with default values.\n * Sets occurredAt to current date and version to 1 if not provided.\n *\n * @param type - The event type\n * @param payload - The event payload\n * @param options - Optional event configuration\n * @returns A domain event\n *\n * @example\n * ```typescript\n * const event = createDomainEvent(\"OrderCreated\", { orderId: \"123\" });\n * ```\n */\nexport function createDomainEvent<T extends string, P>(\n\ttype: T,\n\tpayload: P,\n\toptions?: {\n\t\toccurredAt?: Date;\n\t\tversion?: number;\n\t\tmetadata?: EventMetadata;\n\t},\n): DomainEvent<T, P> {\n\treturn {\n\t\ttype,\n\t\tpayload,\n\t\toccurredAt: options?.occurredAt ?? new Date(),\n\t\tversion: options?.version ?? 1,\n\t\tmetadata: options?.metadata,\n\t};\n}\n\n/**\n * Creates a domain event with metadata for traceability.\n * Convenience function for creating events with correlation and causation IDs.\n *\n * @param type - The event type\n * @param payload - The event payload\n * @param metadata - Event metadata for traceability\n * @param options - Optional event configuration\n * @returns A domain event with metadata\n *\n * @example\n * ```typescript\n * const event = createDomainEventWithMetadata(\n * \"OrderCreated\",\n * { orderId: \"123\" },\n * {\n * correlationId: \"corr-123\",\n * causationId: \"cmd-456\",\n * userId: \"user-789\"\n * }\n * );\n * ```\n */\nexport function createDomainEventWithMetadata<T extends string, P>(\n\ttype: T,\n\tpayload: P,\n\tmetadata: EventMetadata,\n\toptions?: {\n\t\toccurredAt?: Date;\n\t\tversion?: number;\n\t},\n): DomainEvent<T, P> {\n\treturn createDomainEvent(type, payload, {\n\t\t...options,\n\t\tmetadata,\n\t});\n}\n\n/**\n * Copies metadata from a source event to a new event.\n * Useful for maintaining correlation chains in event-driven architectures.\n *\n * @param sourceEvent - The source event to copy metadata from\n * @param additionalMetadata - Additional metadata to merge in\n * @returns Event metadata with copied and merged values\n *\n * @example\n * ```typescript\n * const newEvent = createDomainEvent(\n * \"OrderShipped\",\n * { orderId: \"123\" },\n * {\n * metadata: copyMetadata(previousEvent, { causationId: previousEvent.type })\n * }\n * );\n * ```\n */\nexport function copyMetadata(\n\tsourceEvent: DomainEvent<string, unknown>,\n\tadditionalMetadata?: Partial<EventMetadata>,\n): EventMetadata {\n\treturn {\n\t\t...(sourceEvent.metadata ?? {}),\n\t\t...(additionalMetadata ?? {}),\n\t};\n}\n\n/**\n * Merges multiple metadata objects into one.\n * Later metadata objects override earlier ones for the same keys.\n *\n * @param metadataObjects - Array of metadata objects to merge\n * @returns Merged event metadata\n *\n * @example\n * ```typescript\n * const metadata = mergeMetadata(\n * { correlationId: \"corr-123\" },\n * { userId: \"user-456\" },\n * { source: \"order-service\" }\n * );\n * ```\n */\nexport function mergeMetadata(\n\t...metadataObjects: Array<EventMetadata | undefined>\n): EventMetadata {\n\treturn Object.assign({}, ...metadataObjects.filter(Boolean));\n}\n\n/**\n * Snapshot of an aggregate state at a specific point in time.\n * Used for optimizing event replay by starting from a snapshot\n * instead of replaying all events from the beginning.\n *\n * @template TState - The type of the aggregate state\n */\nexport interface AggregateSnapshot<TState> {\n\t/**\n\t * The state of the aggregate at the time of the snapshot.\n\t */\n\tstate: TState;\n\n\t/**\n\t * The version of the aggregate when the snapshot was taken.\n\t */\n\tversion: Version;\n\n\t/**\n\t * Timestamp when the snapshot was created.\n\t */\n\tsnapshotAt: Date;\n}\n\n/**\n * Checks if two aggregates are the same (same ID and version).\n * Useful for optimistic concurrency control checks.\n *\n * @param a - First aggregate\n * @param b - Second aggregate\n * @returns true if both aggregates have the same ID and version\n *\n * @example\n * ```typescript\n * const aggregate1 = await repository.getById(id);\n * // ... some operations ...\n * const aggregate2 = await repository.getById(id);\n *\n * if (!sameAggregate(aggregate1, aggregate2)) {\n * throw new Error(\"Aggregate was modified by another process\");\n * }\n * ```\n */\nexport function sameAggregate<TId extends Id<string>>(\n\ta: { id: TId; version: Version },\n\tb: { id: TId; version: Version },\n): boolean {\n\treturn a.id === b.id && a.version === b.version;\n}\n","import type { Id } from \"../core/id\";\nimport type {\n\tAggregateRoot,\n\tAggregateSnapshot,\n\tVersion,\n} from \"./aggregate\";\n\n/**\n * Configuration options for AggregateBase behavior.\n */\nexport interface AggregateConfig {\n\t/**\n\t * Whether to automatically bump the version when state changes.\n\t * Defaults to false. Set to true for automatic versioning.\n\t */\n\tautoVersionBump?: boolean;\n}\n\n/**\n * Base class for creating Aggregate Roots (Entities) without Event Sourcing.\n * \n * This class creates an Entity that serves as the Aggregate Root. The Aggregate Root\n * is the parent Entity of the aggregate and represents it externally. It has identity\n * (id) and version for optimistic concurrency control.\n * \n * The aggregate state (`TState`) contains:\n * - Child entities (Entities with id, but no own version)\n * - Value objects (immutable objects)\n * \n * All changes to child entities are versioned through the Aggregate Root. The version\n * applies to the entire aggregate, including all child entities.\n * \n * Provides core functionality:\n * - ID and Version management (for Optimistic Concurrency Control)\n * - State management (containing child entities and value objects)\n * - Snapshot support for performance optimization\n *\n * Implements `AggregateRoot<TId>` to mark this as an Aggregate Root Entity.\n *\n * Use this class when you don't need Event Sourcing but still want\n * aggregate patterns with versioning and state management.\n *\n * @template TState - The type of the aggregate state (contains child entities and value objects)\n * @template TId - The type of the aggregate root identifier\n *\n * @example\n * ```typescript\n * // Order is an Aggregate Root (an Entity)\n * class Order extends AggregateBase<OrderState, OrderId> implements AggregateRoot<OrderId> {\n * constructor(id: OrderId, initialState: OrderState) {\n * super(id, initialState);\n * }\n *\n * confirm(): void {\n * this._state = { ...this._state, status: \"confirmed\" };\n * this.bumpVersion(); // Versions the entire aggregate\n * }\n * }\n * ```\n */\nexport abstract class AggregateBase<TState, TId extends Id<string>>\n\timplements AggregateRoot<TId>\n{\n\tpublic readonly id: TId;\n\tpublic version: Version = 0 as Version;\n\n\tprivate readonly _config: AggregateConfig;\n\tprivate readonly _autoVersionBump: boolean;\n\n\tpublic get state(): TState {\n\t\treturn this._state;\n\t}\n\n\t/**\n\t * The state is 'protected' so that only the subclass can change it.\n\t * Subclasses can mutate this directly or use helper methods.\n\t */\n\tprotected _state: TState;\n\n\tprotected constructor(\n\t\tid: TId,\n\t\tinitialState: TState,\n\t\tconfig?: AggregateConfig,\n\t) {\n\t\tthis.id = id;\n\t\tthis._state = initialState;\n\t\tthis._config = config ?? {};\n\t\tthis._autoVersionBump = this._config.autoVersionBump ?? false;\n\t}\n\n\t/**\n\t * Manually bumps the aggregate version.\n\t * Call this after state changes for Optimistic Concurrency Control.\n\t *\n\t * If `autoVersionBump` is enabled, this is called automatically\n\t * when using `setState()`.\n\t */\n\tprotected bumpVersion(): void {\n\t\tthis.version = (this.version + 1) as Version;\n\t}\n\n\t/**\n\t * Sets the state and optionally bumps the version automatically.\n\t * This is a convenience method for state mutations.\n\t *\n\t * @param newState - The new state\n\t * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)\n\t */\n\tprotected setState(\n\t\tnewState: TState,\n\t\tbumpVersion?: boolean,\n\t): void {\n\t\tthis._state = newState;\n\t\tconst shouldBump = bumpVersion ?? this._autoVersionBump;\n\t\tif (shouldBump) {\n\t\t\tthis.bumpVersion();\n\t\t}\n\t}\n\n\t/**\n\t * Creates a snapshot of the current aggregate state.\n\t * Useful for performance optimization, backup/restore, and audit trails.\n\t *\n\t * @returns A snapshot containing the current state and version\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = aggregate.createSnapshot();\n\t * await snapshotRepository.save(aggregate.id, snapshot);\n\t * ```\n\t */\n\tpublic createSnapshot(): AggregateSnapshot<TState> {\n\t\treturn {\n\t\t\tstate: { ...this._state } as TState,\n\t\t\tversion: this.version,\n\t\t\tsnapshotAt: new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Restores the aggregate from a snapshot.\n\t * This is useful for loading aggregates from snapshots instead of\n\t * rebuilding them from scratch.\n\t *\n\t * @param snapshot - The snapshot to restore from\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = await snapshotRepository.getLatest(aggregateId);\n\t * aggregate.restoreFromSnapshot(snapshot);\n\t * ```\n\t */\n\tpublic restoreFromSnapshot(snapshot: AggregateSnapshot<TState>): void {\n\t\tthis._state = snapshot.state;\n\t\tthis.version = snapshot.version;\n\t}\n}\n","export type Ok<T> = { ok: true; value: T };\nexport type Err<E> = { ok: false; error: E };\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/**\n * Creates an Ok result with a value.\n *\n * @param value - The success value\n * @returns An Ok result containing the value\n *\n * @example\n * ```typescript\n * const result = ok(42);\n * // result is Ok<number>\n * ```\n */\nexport function ok<T>(value: T): Ok<T>;\n\n/**\n * Creates an Ok result with void (no value).\n *\n * @returns An Ok<void> result\n *\n * @example\n * ```typescript\n * const result = ok();\n * // result is Ok<void>\n * ```\n */\nexport function ok(): Ok<void>;\n\nexport function ok<T>(value?: T): Ok<T> {\n\treturn { ok: true, value: value as T };\n}\n\n/**\n * Creates an Err result with an error.\n *\n * @param error - The error value\n * @returns An Err result containing the error\n *\n * @example\n * ```typescript\n * const result = err(\"Something went wrong\");\n * // result is Err<string>\n * ```\n */\nexport function err<E>(error: E): Err<E>;\n\n/**\n * Creates an Err result with void (no error value).\n *\n * @returns An Err<void> result\n *\n * @example\n * ```typescript\n * const result = err();\n * // result is Err<void>\n * ```\n */\nexport function err(): Err<void>;\n\nexport function err<E>(error?: E): Err<E> {\n\treturn { ok: false, error: error as E };\n}\n\n/**\n * Type guard to check if a Result is Ok.\n * Narrows the type to Ok<T> when returning true.\n *\n * @param result - The result to check\n * @returns true if the result is Ok, false otherwise\n *\n * @example\n * ```typescript\n * const result = voWithValidation(data, validator);\n * if (isOk(result)) {\n * // TypeScript knows result is Ok<ValueObject<T>>\n * console.log(result.value);\n * }\n * ```\n */\nexport function isOk<T, E>(result: Result<T, E>): result is Ok<T> {\n\treturn result.ok === true;\n}\n\n/**\n * Type guard to check if a Result is Err.\n * Narrows the type to Err<E> when returning true.\n *\n * @param result - The result to check\n * @returns true if the result is Err, false otherwise\n *\n * @example\n * ```typescript\n * const result = voWithValidation(data, validator);\n * if (isErr(result)) {\n * // TypeScript knows result is Err<string>\n * console.error(result.error);\n * }\n * ```\n */\nexport function isErr<T, E>(result: Result<T, E>): result is Err<E> {\n\treturn result.ok === false;\n}\n\n/**\n * Chains Result operations (flatMap/bind).\n * If the result is Ok, applies the function to the value.\n * If Err, returns the error unchanged.\n *\n * @param result - The result to chain\n * @param fn - Function that takes the Ok value and returns a new Result\n * @returns A new Result\n *\n * @example\n * ```typescript\n * const result = validateUserId(\"123\")\n * .andThen(userId => validateEmail(\"test@example.com\")\n * .map(email => ({ id: userId, email }))\n * );\n * ```\n */\nexport function andThen<T, E, U>(\n\tresult: Result<T, E>,\n\tfn: (value: T) => Result<U, E>,\n): Result<U, E> {\n\tif (result.ok) {\n\t\treturn fn(result.value);\n\t}\n\treturn result;\n}\n\n/**\n * Transforms the Ok value using a function.\n * If the result is Err, returns the error unchanged.\n *\n * @param result - The result to transform\n * @param fn - Function to transform the Ok value\n * @returns A new Result with transformed value\n *\n * @example\n * ```typescript\n * const result = ok(5);\n * const doubled = map(result, x => x * 2); // Ok<10>\n * ```\n */\nexport function map<T, E, U>(\n\tresult: Result<T, E>,\n\tfn: (value: T) => U,\n): Result<U, E> {\n\tif (result.ok) {\n\t\treturn ok(fn(result.value));\n\t}\n\treturn result;\n}\n\n/**\n * Transforms the Err value using a function.\n * If the result is Ok, returns the value unchanged.\n *\n * @param result - The result to transform\n * @param fn - Function to transform the Err value\n * @returns A new Result with transformed error\n *\n * @example\n * ```typescript\n * const result = err(\"not found\");\n * const mapped = mapErr(result, e => `Error: ${e}`); // Err<\"Error: not found\">\n * ```\n */\nexport function mapErr<T, E, F>(\n\tresult: Result<T, E>,\n\tfn: (error: E) => F,\n): Result<T, F> {\n\tif (result.ok) {\n\t\treturn result;\n\t}\n\treturn err(fn(result.error));\n}\n\n/**\n * Returns the value if Ok, otherwise returns the default value.\n *\n * @param result - The result to unwrap\n * @param defaultValue - Default value to return if Err\n * @returns The Ok value or the default value\n *\n * @example\n * ```typescript\n * const result = validateUserId(\"123\");\n * const userId = unwrapOr(result, \"default-id\");\n * ```\n */\nexport function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {\n\treturn result.ok ? result.value : defaultValue;\n}\n\n/**\n * Returns the value if Ok, otherwise computes default from error.\n *\n * @param result - The result to unwrap\n * @param fn - Function to compute default from error\n * @returns The Ok value or computed default\n *\n * @example\n * ```typescript\n * const result = validateUserId(\"\");\n * const userId = unwrapOrElse(result, err => `fallback-${Date.now()}`);\n * ```\n */\nexport function unwrapOrElse<T, E>(\n\tresult: Result<T, E>,\n\tfn: (error: E) => T,\n): T {\n\treturn result.ok ? result.value : fn(result.error);\n}\n\n/**\n * Pattern matching for Result.\n * Applies one function if Ok, another if Err.\n *\n * @param result - The result to match\n * @param onOk - Function to apply if Ok\n * @param onErr - Function to apply if Err\n * @returns The result of applying the appropriate function\n *\n * @example\n * ```typescript\n * const message = match(result,\n * value => `Success: ${value}`,\n * error => `Error: ${error}`\n * );\n * ```\n */\nexport function match<T, E, R>(\n\tresult: Result<T, E>,\n\tonOk: (value: T) => R,\n\tonErr: (error: E) => R,\n): R {\n\treturn result.ok ? onOk(result.value) : onErr(result.error);\n}\n\n","import {\n\tandThen,\n\terr,\n\tisErr,\n\tisOk,\n\tmap,\n\tmapErr,\n\tmatch,\n\tok,\n\ttype Result,\n\tunwrapOr,\n\tunwrapOrElse,\n} from \"./result\";\n\n/**\n * Base class for Result with method chaining support.\n * Provides common methods for both Ok and Err classes.\n */\nabstract class ResultBase<T, E> {\n\tprotected readonly _result: Result<T, E>;\n\n\tprotected constructor(result: Result<T, E>) {\n\t\tthis._result = result;\n\t}\n\n\t/**\n\t * Returns the value if Ok, otherwise throws an error.\n\t * If error is an Error instance, throws it directly.\n\t * Otherwise, wraps the error in a new Error.\n\t *\n\t * @throws Error if the result is Err\n\t */\n\tunwrap(): T {\n\t\tif (this._result.ok) {\n\t\t\treturn this._result.value;\n\t\t}\n\t\tif (this._result.error instanceof Error) {\n\t\t\tthrow this._result.error;\n\t\t}\n\t\tthrow new Error(String(this._result.error));\n\t}\n\n\t/**\n\t * Returns the value if Ok, otherwise returns the default value.\n\t */\n\tunwrapOr(defaultValue: T): T {\n\t\treturn unwrapOr(this._result, defaultValue);\n\t}\n\n\t/**\n\t * Returns the value if Ok, otherwise computes default from error.\n\t */\n\tunwrapOrElse(fn: (error: E) => T): T {\n\t\treturn unwrapOrElse(this._result, fn);\n\t}\n\n\t/**\n\t * Pattern matching for Result.\n\t * Applies one function if Ok, another if Err.\n\t */\n\tmatch<R>(onOk: (value: T) => R, onErr: (error: E) => R): R {\n\t\treturn match(this._result, onOk, onErr);\n\t}\n\n\t/**\n\t * Type guard to check if the result is Ok.\n\t */\n\tisOk(): this is Success<T> {\n\t\treturn isOk(this._result);\n\t}\n\n\t/**\n\t * Type guard to check if the result is Err.\n\t */\n\tisErr(): this is Erroneous<E> {\n\t\treturn isErr(this._result);\n\t}\n\n\t/**\n\t * Gets the underlying Result value.\n\t */\n\tget result(): Result<T, E> {\n\t\treturn this._result;\n\t}\n}\n\n/**\n * Class representing a successful result with method chaining support.\n * Use this for class-based API with method chaining.\n *\n * @example\n * ```typescript\n * const okResult = Ok(1);\n * const doubled = okResult.map(x => x * 2).unwrap(); // 2\n *\n * const chained = Ok(5)\n * .andThen(x => Ok(x * 2))\n * .map(x => x + 1)\n * .unwrap(); // 11\n * ```\n */\nclass Success<T> extends ResultBase<T, never> {\n\tconstructor(value: T) {\n\t\tsuper(ok(value));\n\t}\n\n\t/**\n\t * Factory function to create an Ok instance.\n\t * Can be called with or without `new`.\n\t */\n\tstatic of<T>(value: T): Success<T> {\n\t\treturn new Success(value);\n\t}\n\n\t/**\n\t * Chains Result operations (flatMap/bind).\n\t * If the result is Ok, applies the function to the value.\n\t * If Err, returns the error unchanged.\n\t */\n\tandThen<U, E>(fn: (value: T) => Result<U, E>): Success<U> | Erroneous<E> {\n\t\tconst result = andThen(this._result, fn);\n\t\tif (result.ok) {\n\t\t\treturn new Success(result.value);\n\t\t}\n\t\treturn new Erroneous(result.error);\n\t}\n\n\t/**\n\t * Transforms the Ok value using a function.\n\t * If the result is Err, returns the error unchanged.\n\t */\n\tmap<U>(fn: (value: T) => U): Success<U> {\n\t\tconst result = map(this._result, fn);\n\t\tif (result.ok) {\n\t\t\treturn new Success(result.value);\n\t\t}\n\t\t// This should never happen for Success, but TypeScript needs it\n\t\tthrow new Error(\"Unexpected error in Success.map\");\n\t}\n\n\t/**\n\t * Transforms the Err value using a function.\n\t * If the result is Ok, returns the value unchanged.\n\t */\n\tmapErr<F>(_fn: (error: never) => F): Success<T> {\n\t\treturn this;\n\t}\n}\n\nexport { Success };\n\n/**\n * Class representing an erroneous result with method chaining support.\n * Use this for class-based API with method chaining.\n *\n * @example\n * ```typescript\n * const errResult = Err(new Error(\"error\"));\n * errResult.map(x => x * 2).unwrap(); // throws Error(\"error\")\n *\n * const mapped = Err(\"original\")\n * .mapErr(e => `Error: ${e}`)\n * .unwrap(); // throws Error(\"Error: original\")\n * ```\n */\nclass Erroneous<E> extends ResultBase<never, E> {\n\tconstructor(error: E) {\n\t\tsuper(err(error));\n\t}\n\n\t/**\n\t * Factory function to create an Err instance.\n\t * Can be called with or without `new`.\n\t */\n\tstatic of<E>(error: E): Erroneous<E> {\n\t\treturn new Erroneous(error);\n\t}\n\n\t/**\n\t * Chains Result operations (flatMap/bind).\n\t * If the result is Ok, applies the function to the value.\n\t * If Err, returns the error unchanged.\n\t */\n\tandThen<U>(_fn: (value: never) => Result<U, E>): Erroneous<E> {\n\t\treturn this;\n\t}\n\n\t/**\n\t * Transforms the Ok value using a function.\n\t * If the result is Err, returns the error unchanged.\n\t */\n\tmap<U>(_fn: (value: never) => U): Erroneous<E> {\n\t\treturn this;\n\t}\n\n\t/**\n\t * Transforms the Err value using a function.\n\t * If the result is Ok, returns the value unchanged.\n\t */\n\tmapErr<F>(fn: (error: E) => F): Erroneous<F> {\n\t\tconst result = mapErr(this._result, fn);\n\t\tif (!result.ok) {\n\t\t\treturn new Erroneous(result.error);\n\t\t}\n\t\t// This should never happen for Erroneous, but TypeScript needs it\n\t\tthrow new Error(\"Unexpected ok in Erroneous.mapErr\");\n\t}\n}\n\nexport { Erroneous };\n\n/**\n * Factory functions for creating Ok and Err instances.\n * These can be called without `new` for a more functional style.\n *\n * @example\n * ```typescript\n * const okResult = Ok(1);\n * const errResult = Err(\"error\");\n * ```\n */\nexport function Ok<T>(value: T): Success<T> {\n\treturn new Success(value);\n}\n\nexport function Err<E>(error: E): Erroneous<E> {\n\treturn new Erroneous(error);\n}\n\n/**\n * Class-based API for Result with method chaining support.\n * Provides an object-oriented alternative to the functional Result type.\n * Use `Outcome.from()` to wrap an existing Result, or `Outcome.ok()` / `Outcome.err()` to create new instances.\n *\n * @example\n * ```typescript\n * // Wrap an existing Result\n * const result = Outcome.from(ok(1));\n * const doubled = result.map(x => x * 2).unwrap(); // 2\n *\n * // Create directly\n * const outcome = Outcome.ok(42);\n * const value = outcome.map(x => x + 1).unwrap(); // 43\n * ```\n */\nexport class Outcome<T, E> extends ResultBase<T, E> {\n\tprivate constructor(result: Result<T, E>) {\n\t\tsuper(result);\n\t}\n\n\t/**\n\t * Creates an Outcome from an Ok value.\n\t */\n\tstatic ok<T>(value: T): Success<T> {\n\t\treturn new Success(value);\n\t}\n\n\t/**\n\t * Creates an Outcome from an Err value.\n\t */\n\tstatic err<E>(error: E): Erroneous<E> {\n\t\treturn new Erroneous(error);\n\t}\n\n\t/**\n\t * Creates an Outcome from a Result.\n\t */\n\tstatic from<T, E>(result: Result<T, E>): Outcome<T, E> {\n\t\treturn new Outcome(result);\n\t}\n\n\tandThen<U>(fn: (value: T) => Result<U, E>): Outcome<U, E> {\n\t\treturn Outcome.from(andThen(this._result, fn));\n\t}\n\n\tmap<U>(fn: (value: T) => U): Outcome<U, E> {\n\t\treturn Outcome.from(map(this._result, fn));\n\t}\n\n\tmapErr<F>(fn: (error: E) => F): Outcome<T, F> {\n\t\treturn Outcome.from(mapErr(this._result, fn));\n\t}\n}\n\n","import { err, ok, type Result } from \"../core/result\";\nimport type { Id } from \"../core/id\";\nimport { AggregateBase, type AggregateConfig } from \"./aggregate-base\";\nimport type {\n\tAggregateSnapshot,\n\tDomainEvent,\n\tVersion,\n} from \"./aggregate\";\n\ntype Handler<TState, TEvent> = (state: TState, event: TEvent) => TState;\n\n/**\n * Extended configuration options for AggregateEventSourced behavior.\n */\nexport interface AggregateEventSourcedConfig extends AggregateConfig {\n\t/**\n\t * Whether to automatically bump the version when applying new events.\n\t * Defaults to true. Set to false to manually control versioning.\n\t */\n\tautoVersionBump?: boolean;\n}\n\n/**\n * Base class for Event-Sourced Aggregate Roots (Entities).\n * \n * Extends `AggregateBase` to create an Aggregate Root Entity with Event Sourcing capabilities.\n * The Aggregate Root is the parent Entity of the aggregate and represents it externally.\n * \n * The aggregate state (`TState`) contains:\n * - Child entities (Entities with id, but no own version)\n * - Value objects (immutable objects)\n * \n * All changes to child entities are versioned through the Aggregate Root. The version\n * applies to the entire aggregate, including all child entities.\n * \n * Extends `AggregateBase` with Event Sourcing capabilities:\n * - Event tracking (pendingEvents)\n * - Event handlers for state transitions\n * - Event validation\n * - History replay\n *\n * Use this class when you want Event Sourcing with full event tracking\n * and replay capabilities.\n *\n * @template TState - The type of the aggregate state (contains child entities and value objects)\n * @template TEvent - The union type of all domain events\n * @template TId - The type of the aggregate root identifier\n *\n * @example\n * ```typescript\n * // Order is an Aggregate Root (an Entity) with Event Sourcing\n * class Order extends AggregateEventSourced<OrderState, OrderEvent, OrderId> {\n * confirm(): void {\n * this.apply(createDomainEvent(\"OrderConfirmed\", {}));\n * }\n *\n * protected readonly handlers = {\n * OrderConfirmed: (state: OrderState): OrderState => ({\n * ...state,\n * status: \"confirmed\",\n * }),\n * };\n * }\n * ```\n */\nexport abstract class AggregateEventSourced<\n\tTState,\n\tTEvent extends DomainEvent<string, unknown>,\n\tTId extends Id<string>,\n> extends AggregateBase<TState, TId> {\n\tprivate readonly _eventConfig: AggregateEventSourcedConfig;\n\tprivate readonly _eventAutoVersionBump: boolean;\n\n\tprivate readonly _pendingEvents: TEvent[] = [];\n\n\tprotected constructor(\n\t\tid: TId,\n\t\tinitialState: TState,\n\t\tconfig?: AggregateEventSourcedConfig,\n\t) {\n\t\tsuper(id, initialState, config);\n\t\tthis._eventConfig = config ?? {};\n\t\tthis._eventAutoVersionBump = this._eventConfig.autoVersionBump ?? true;\n\t}\n\n\t/**\n\t * Returns a read-only list of new, not-yet-persisted events.\n\t */\n\tpublic get pendingEvents(): ReadonlyArray<TEvent> {\n\t\treturn this._pendingEvents;\n\t}\n\n\t/**\n\t * Clears the list of pending events.\n\t * Typically called after the events have been persisted.\n\t */\n\tpublic clearPendingEvents(): void {\n\t\tthis._pendingEvents.length = 0;\n\t}\n\n\t/**\n\t * Validates an event before it is applied.\n\t * Override this method to add custom validation logic.\n\t * Return `ok(true)` if the event is valid, `err(message)` otherwise.\n\t *\n\t * @param event - The event to validate\n\t * @returns Result indicating if the event is valid\n\t *\n\t * @example\n\t * ```typescript\n\t * protected validateEvent(event: OrderEvent): Result<true, string> {\n\t * if (event.type === \"OrderShipped\" && this.state.status !== \"confirmed\") {\n\t * return err(\"Order must be confirmed before shipping\");\n\t * }\n\t * return ok(true);\n\t * }\n\t * ```\n\t */\n\tprotected validateEvent(_event: TEvent): Result<true, string> {\n\t\treturn ok(true);\n\t}\n\n\t/**\n\t * Applies an event to change the state and adds it\n\t * to the list of pending events.\n\t * Returns a Result type instead of throwing an error.\n\t *\n\t * @param event - The domain event to apply\n\t * @param isNew - Indicates whether the event is new (and needs to be persisted)\n\t * or if it is being loaded from history\n\t * @returns Result<void, string> - ok if successful, err with error message if validation fails or handler is missing\n\t */\n\tprotected apply(event: TEvent, isNew = true): Result<void, string> {\n\t\t// Validate event before applying\n\t\tconst validation = this.validateEvent(event);\n\t\tif (!validation.ok) {\n\t\t\treturn err(\n\t\t\t\t`Event validation failed for ${event.type}: ${validation.error}`,\n\t\t\t);\n\t\t}\n\n\t\tconst handler = this.handlers[event.type as keyof typeof this.handlers];\n\t\tif (!handler) {\n\t\t\treturn err(`Missing handler for event type: ${event.type}`);\n\t\t}\n\n\t\t// First, change the state\n\t\tthis._state = handler(\n\t\t\tthis._state,\n\t\t\tevent as Extract<TEvent, { type: TEvent[\"type\"] }>,\n\t\t);\n\n\t\t// Then (if new) add the event to the list and bump version\n\t\tif (isNew) {\n\t\t\tthis._pendingEvents.push(event);\n\t\t\tif (this._eventAutoVersionBump) {\n\t\t\t\tthis.version = (this.version + 1) as Version;\n\t\t\t}\n\t\t}\n\n\t\treturn ok();\n\t}\n\n\t/**\n\t * Applies an event to change the state and adds it\n\t * to the list of pending events.\n\t * Throws an error if validation fails or handler is missing.\n\t *\n\t * @param event - The domain event to apply\n\t * @param isNew - Indicates whether the event is new (and needs to be persisted)\n\t * or if it is being loaded from history\n\t * @throws Error if event validation fails or handler is missing\n\t */\n\tprotected applyUnsafe(event: TEvent, isNew = true): void {\n\t\t// Validate event before applying\n\t\tconst validation = this.validateEvent(event);\n\t\tif (!validation.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Event validation failed for ${event.type}: ${validation.error}`,\n\t\t\t);\n\t\t}\n\n\t\tconst handler = this.handlers[event.type as keyof typeof this.handlers];\n\t\tif (!handler) {\n\t\t\tthrow new Error(`Missing handler for event type: ${event.type}`);\n\t\t}\n\n\t\t// First, change the state\n\t\tthis._state = handler(\n\t\t\tthis._state,\n\t\t\tevent as Extract<TEvent, { type: TEvent[\"type\"] }>,\n\t\t);\n\n\t\t// Then (if new) add the event to the list and bump version\n\t\tif (isNew) {\n\t\t\tthis._pendingEvents.push(event);\n\t\t\tif (this._eventAutoVersionBump) {\n\t\t\t\tthis.version = (this.version + 1) as Version;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manually bumps the aggregate version.\n\t * Only needed if `autoVersionBump` is disabled.\n\t */\n\tprotected bumpVersion(): void {\n\t\tthis.version = (this.version + 1) as Version;\n\t}\n\n\t/**\n\t * Reconstitutes the aggregate from an event history.\n\t * Sets the version to the number of events in the history.\n\t *\n\t * @param history - An ordered list of past events\n\t */\n\tpublic loadFromHistory(history: TEvent[]): Result<void, string> {\n\t\tfor (const event of history) {\n\t\t\tconst result = this.apply(event, false); // 'false' as it's not a new event\n\t\t\tif (!result.ok) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\t// Set version to the number of events in history\n\t\tthis.version = history.length as Version;\n\t\treturn ok();\n\t}\n\n\t/**\n\t * Checks if the aggregate has any pending events.\n\t *\n\t * @returns true if there are pending events, false otherwise\n\t */\n\tpublic hasPendingEvents(): boolean {\n\t\treturn this._pendingEvents.length > 0;\n\t}\n\n\t/**\n\t * Returns the number of pending events.\n\t *\n\t * @returns The count of pending events\n\t */\n\tpublic getEventCount(): number {\n\t\treturn this._pendingEvents.length;\n\t}\n\n\t/**\n\t * Returns the latest pending event, if any.\n\t *\n\t * @returns The most recent event or undefined if no events exist\n\t */\n\tpublic getLatestEvent(): TEvent | undefined {\n\t\treturn this._pendingEvents[this._pendingEvents.length - 1];\n\t}\n\n\t/**\n\t * Restores the aggregate from a snapshot and applies events that occurred after the snapshot.\n\t * This is more efficient than replaying all events from the beginning.\n\t *\n\t * @param snapshot - The snapshot to restore from\n\t * @param eventsAfterSnapshot - Events that occurred after the snapshot was taken\n\t *\n\t * @example\n\t * ```typescript\n\t * const snapshot = await snapshotRepository.getLatest(aggregateId);\n\t * const eventsAfter = await eventStore.getEventsAfter(aggregateId, snapshot.version);\n\t * aggregate.restoreFromSnapshotWithEvents(snapshot, eventsAfter);\n\t * ```\n\t */\n\tpublic restoreFromSnapshotWithEvents(\n\t\tsnapshot: AggregateSnapshot<TState>,\n\t\teventsAfterSnapshot: TEvent[],\n\t): Result<void, string> {\n\t\tthis._state = snapshot.state;\n\t\tthis.version = snapshot.version;\n\n\t\t// Apply events that occurred after the snapshot\n\t\tfor (const event of eventsAfterSnapshot) {\n\t\t\tconst result = this.apply(event, false);\n\t\t\tif (!result.ok) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\t// Set version to snapshot version + events after snapshot\n\t\tthis.version = (snapshot.version + eventsAfterSnapshot.length) as Version;\n\t\treturn ok();\n\t}\n\n\t/**\n\t * A map of event types to their corresponding handlers.\n\t * Subclasses MUST implement this property.\n\t */\n\tprotected abstract readonly handlers: {\n\t\t[K in TEvent[\"type\"]]: Handler<TState, Extract<TEvent, { type: K }>>;\n\t};\n}\n\n","import type { Result } from \"../core/result\";\nimport { err } from \"../core/result\";\nimport type { Command, CommandHandler } from \"./command\";\n\n/**\n * Command Bus interface for dispatching commands to their handlers.\n * Provides a centralized way to execute commands with handler registration.\n *\n * @example\n * ```typescript\n * const bus = new CommandBus();\n * bus.register(\"CreateOrder\", createOrderHandler);\n *\n * const result = await bus.execute({\n * type: \"CreateOrder\",\n * customerId: \"123\",\n * items: [...]\n * });\n * ```\n */\nexport interface ICommandBus {\n\t/**\n\t * Executes a command by dispatching it to the registered handler.\n\t *\n\t * @param command - The command to execute\n\t * @returns Result containing the success value or error message\n\t */\n\texecute<C extends Command, R>(command: C): Promise<Result<R, string>>;\n\n\t/**\n\t * Registers a handler for a specific command type.\n\t *\n\t * @param commandType - The command type to register the handler for\n\t * @param handler - The handler function for this command type\n\t */\n\tregister<C extends Command, R>(\n\t\tcommandType: C[\"type\"],\n\t\thandler: CommandHandler<C, R>,\n\t): void;\n}\n\n/**\n * Simple in-memory command bus implementation.\n * Handlers are stored in a Map and dispatched based on command type.\n *\n * **Note:** This is a basic implementation suitable for development and simple use cases.\n * For production environments, consider implementing or using a more feature-rich bus that includes:\n * - Middleware/Pipeline support (logging, validation, authorization)\n * - Error handling and retry logic\n * - Timeout handling\n * - Metrics and observability\n * - Transaction management\n * - Dead letter queue support\n *\n * The `CommandHandler` type can still be used with external production-grade buses\n * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.\n *\n * @example\n * ```typescript\n * const bus = new CommandBus();\n * bus.register(\"CreateOrder\", async (cmd) => {\n * // ... handler logic\n * return ok(orderId);\n * });\n *\n * const result = await bus.execute({ type: \"CreateOrder\", ... });\n * ```\n */\nexport class CommandBus implements ICommandBus {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, CommandHandler<any, any>>();\n\n\tregister<C extends Command, R>(\n\t\tcommandType: C[\"type\"],\n\t\thandler: CommandHandler<C, R>,\n\t): void {\n\t\tthis.handlers.set(commandType, handler);\n\t}\n\n\tasync execute<C extends Command, R>(\n\t\tcommand: C,\n\t): Promise<Result<R, string>> {\n\t\tconst handler = this.handlers.get(command.type);\n\t\tif (!handler) {\n\t\t\treturn err(`No handler registered for command type: ${command.type}`);\n\t\t}\n\t\treturn handler(command);\n\t}\n}\n\n","import type { EventBus, Outbox } from \"../events/ports\";\nimport type { UnitOfWork } from \"../repo/uow\";\n\n/**\n * Helper function for executing commands within a transaction.\n * Handles event persistence via outbox and optional event bus publishing.\n *\n * @param deps - Dependencies including outbox, optional event bus, and unit of work\n * @param fn - Function that returns result and events\n * @returns The result wrapped in a transaction\n *\n * @example\n * ```typescript\n * const result = await withCommit(\n * { outbox, bus, uow },\n * async () => {\n * const order = Order.create(customerId, items);\n * await repository.save(order);\n * return {\n * result: order.id,\n * events: order.pendingEvents\n * };\n * }\n * );\n * ```\n */\nexport function withCommit<Evt, R>(\n\tdeps: {\n\t\toutbox: Outbox<Evt>;\n\t\tbus?: EventBus<Evt>;\n\t\tuow: UnitOfWork;\n\t},\n\tfn: () => Promise<{ result: R; events: ReadonlyArray<Evt> }>,\n) {\n\treturn deps.uow.transactional(async () => {\n\t\tconst { result, events } = await fn();\n\t\tawait deps.outbox.add(events);\n\t\tif (deps.bus) await deps.bus.publish(events);\n\t\treturn result;\n\t});\n}\n","import { err, ok, type Result } from \"../core/result\";\nimport type { Query, QueryHandler } from \"./query\";\n\n/**\n * Query Bus interface for dispatching queries to their handlers.\n * Provides a centralized way to execute queries with handler registration.\n *\n * @example\n * ```typescript\n * const bus = new QueryBus();\n * bus.register(\"GetOrder\", getOrderHandler);\n *\n * const order = await bus.execute({\n * type: \"GetOrder\",\n * orderId: \"123\"\n * });\n * ```\n */\nexport interface IQueryBus {\n\t/**\n\t * Executes a query by dispatching it to the registered handler.\n\t * Returns a Result type instead of throwing an error.\n\t *\n\t * @param query - The query to execute\n\t * @returns Result containing the query result if successful, or an error message if no handler is registered\n\t */\n\texecute<Q extends Query, R>(query: Q): Promise<Result<R, string>>;\n\n\t/**\n\t * Executes a query by dispatching it to the registered handler.\n\t * Throws an error if no handler is registered.\n\t *\n\t * @param query - The query to execute\n\t * @returns The query result\n\t * @throws Error if no handler is registered for the query type\n\t */\n\texecuteUnsafe<Q extends Query, R>(query: Q): Promise<R>;\n\n\t/**\n\t * Registers a handler for a specific query type.\n\t *\n\t * @param queryType - The query type to register the handler for\n\t * @param handler - The handler function for this query type\n\t */\n\tregister<Q extends Query, R>(\n\t\tqueryType: Q[\"type\"],\n\t\thandler: QueryHandler<Q, R>,\n\t): void;\n}\n\n/**\n * Simple in-memory query bus implementation.\n * Handlers are stored in a Map and dispatched based on query type.\n *\n * **Note:** This is a basic implementation suitable for development and simple use cases.\n * For production environments, consider implementing or using a more feature-rich bus that includes:\n * - Middleware/Pipeline support (logging, caching, rate limiting)\n * - Error handling\n * - Timeout handling\n * - Metrics and observability\n * - Query result caching\n * - Rate limiting\n *\n * The `QueryHandler` type can still be used with external production-grade buses\n * (e.g., RabbitMQ, AWS SQS) while maintaining type safety.\n *\n * @example\n * ```typescript\n * const bus = new QueryBus();\n * bus.register(\"GetOrder\", async (query) => {\n * return await repository.getById(query.orderId);\n * });\n *\n * const order = await bus.execute({ type: \"GetOrder\", orderId: \"123\" });\n * ```\n */\nexport class QueryBus implements IQueryBus {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, QueryHandler<any, any>>();\n\n\tregister<Q extends Query, R>(\n\t\tqueryType: Q[\"type\"],\n\t\thandler: QueryHandler<Q, R>,\n\t): void {\n\t\tthis.handlers.set(queryType, handler);\n\t}\n\n\tasync execute<Q extends Query, R>(query: Q): Promise<Result<R, string>> {\n\t\tconst handler = this.handlers.get(query.type);\n\t\tif (!handler) {\n\t\t\treturn err(`No handler registered for query type: ${query.type}`);\n\t\t}\n\t\ttry {\n\t\t\tconst result = await handler(query);\n\t\t\treturn ok(result);\n\t\t} catch (error) {\n\t\t\treturn err(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t);\n\t\t}\n\t}\n\n\tasync executeUnsafe<Q extends Query, R>(query: Q): Promise<R> {\n\t\tconst handler = this.handlers.get(query.type);\n\t\tif (!handler) {\n\t\t\tthrow new Error(`No handler registered for query type: ${query.type}`);\n\t\t}\n\t\treturn handler(query);\n\t}\n}\n\n","import { err, ok, type Result } from \"./result\";\n\n/**\n * Guard function that validates a condition and returns a Result.\n * Returns `ok(true)` if the condition is met, otherwise `err(error)`.\n *\n * @param cond - The condition to check\n * @param error - Error message if condition fails\n * @returns Result<true, string>\n *\n * @example\n * ```typescript\n * const result = guard(id.length > 0, \"ID cannot be empty\");\n * if (!result.ok) {\n * return err(result.error);\n * }\n * ```\n */\nexport function guard(cond: boolean, error: string): Result<true, string> {\n\treturn cond ? ok(true) : err(error);\n}\n","/**\n * Interface for entities with identity.\n * \n * In Domain-Driven Design, there are two types of entities:\n * \n * 1. **Aggregate Root Entity**: The parent Entity of an aggregate.\n * - Has identity (id) and version\n * - Implemented by classes extending `AggregateBase` or `AggregateEventSourced`\n * - Represents the aggregate externally\n * - Loaded/saved through repositories\n * \n * 2. **Child Entities**: Entities within an aggregate.\n * - Have identity (id), but no own version\n * - Exist only within the aggregate boundary\n * - Versioned through the Aggregate Root\n * - Cannot be referenced directly from outside the aggregate\n * \n * This interface is used for child entities within aggregates. The Aggregate Root\n * also implements this interface (through `AggregateRoot<TId>`), but additionally\n * has version management.\n *\n * @template TId - The type of the entity identifier\n *\n * @example\n * ```typescript\n * // Child Entity within an aggregate\n * type OrderItem = Entity<ItemId> & {\n * productId: string;\n * quantity: number;\n * };\n * \n * // Aggregate Root (also an Entity, but with version)\n * class Order extends AggregateBase<OrderState, OrderId> \n * implements AggregateRoot<OrderId> {\n * // Order is an Entity (the Aggregate Root)\n * // OrderState contains OrderItem (child entities)\n * }\n * ```\n */\nexport interface Entity<TId> {\n\treadonly id: TId;\n}\n\n/**\n * Checks if two entities have the same ID.\n * Works with any object that has an 'id' property.\n *\n * @param a - First entity\n * @param b - Second entity\n * @returns true if both entities have the same ID, false otherwise\n *\n * @example\n * ```typescript\n * const item1: OrderItem = { id: itemId1, productId: \"prod-1\", quantity: 2 };\n * const item2: OrderItem = { id: itemId2, productId: \"prod-2\", quantity: 1 };\n *\n * sameEntity(item1, item2); // false\n * sameEntity(item1, item1); // true\n * ```\n */\nexport function sameEntity<TId>(a: { id: TId }, b: { id: TId }): boolean {\n\treturn a.id === b.id;\n}\n\n/**\n * Finds an entity by ID in a collection.\n * Returns undefined if not found.\n *\n * @param entities - Array of entities to search\n * @param id - The ID to search for\n * @returns The entity if found, undefined otherwise\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const item = findEntityById(items, itemId1);\n * // item is { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ```\n */\nexport function findEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): T | undefined {\n\treturn entities.find((entity) => entity.id === id);\n}\n\n/**\n * Checks if an entity with the given ID exists in the collection.\n *\n * @param entities - Array of entities to search\n * @param id - The ID to check for\n * @returns true if an entity with the ID exists, false otherwise\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * hasEntityId(items, itemId1); // true\n * hasEntityId(items, itemId2); // false\n * ```\n */\nexport function hasEntityId<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): boolean {\n\treturn entities.some((entity) => entity.id === id);\n}\n\n/**\n * Removes an entity with the given ID from the collection.\n * Returns a new array without the entity.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to remove\n * @returns A new array without the entity with the given ID\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const updated = removeEntityById(items, itemId1);\n * // updated is [{ id: itemId2, productId: \"prod-2\", quantity: 1 }]\n * ```\n */\nexport function removeEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n): T[] {\n\treturn entities.filter((entity) => entity.id !== id);\n}\n\n/**\n * Updates an entity with the given ID in the collection.\n * Returns a new array with the updated entity.\n * If the entity is not found, returns the original array unchanged.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to update\n * @param updater - Function that takes the entity and returns the updated entity\n * @returns A new array with the updated entity\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * const updated = updateEntityById(items, itemId1, (item) => ({\n * ...item,\n * quantity: item.quantity + 1\n * }));\n * // updated is [{ id: itemId1, productId: \"prod-1\", quantity: 3 }]\n * ```\n */\nexport function updateEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n\tupdater: (entity: T) => T,\n): T[] {\n\treturn entities.map((entity) => (entity.id === id ? updater(entity) : entity));\n}\n\n/**\n * Replaces an entity with the given ID in the collection.\n * Returns a new array with the replaced entity.\n * If the entity is not found, returns the original array unchanged.\n *\n * @param entities - Array of entities\n * @param id - The ID of the entity to replace\n * @param replacement - The replacement entity\n * @returns A new array with the replaced entity\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 }\n * ];\n *\n * const updated = replaceEntityById(items, itemId1, {\n * id: itemId1,\n * productId: \"prod-1\",\n * quantity: 5\n * });\n * ```\n */\nexport function replaceEntityById<TId, T extends { id: TId }>(\n\tentities: T[],\n\tid: TId,\n\treplacement: T,\n): T[] {\n\treturn entities.map((entity) => (entity.id === id ? replacement : entity));\n}\n\n/**\n * Extracts all IDs from a collection of entities.\n *\n * @param entities - Array of entities\n * @returns Array of entity IDs\n *\n * @example\n * ```typescript\n * const items: OrderItem[] = [\n * { id: itemId1, productId: \"prod-1\", quantity: 2 },\n * { id: itemId2, productId: \"prod-2\", quantity: 1 }\n * ];\n *\n * const ids = entityIds(items);\n * // ids is [itemId1, itemId2]\n * ```\n */\nexport function entityIds<TId, T extends { id: TId }>(entities: T[]): TId[] {\n\treturn entities.map((entity) => entity.id);\n}\n\n","import type { DomainEvent } from \"../aggregate/aggregate\";\nimport type { EventBus, EventHandler } from \"./ports\";\n\n/**\n * Simple in-memory event bus implementation.\n * Supports multiple subscribers per event type (pub/sub pattern).\n *\n * @template Evt - The type of domain events (must extend DomainEvent)\n *\n * @example\n * ```typescript\n * const bus = new EventBusImpl<OrderEvent>();\n *\n * bus.subscribe(\"OrderCreated\", async (event) => {\n * await sendEmail(event.payload.customerId);\n * });\n *\n * bus.subscribe(\"OrderCreated\", async (event) => {\n * await logEvent(event);\n * });\n *\n * await bus.publish([orderCreatedEvent]);\n * // Both handlers will be called\n * ```\n */\nexport class EventBusImpl<Evt extends DomainEvent<string, unknown>>\n\timplements EventBus<Evt>\n{\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate readonly handlers = new Map<string, Set<EventHandler<any>>>();\n\n\tsubscribe<T extends Evt>(\n\t\teventType: string,\n\t\thandler: EventHandler<T>,\n\t): () => void {\n\t\tconst type = eventType;\n\t\tif (!this.handlers.has(type)) {\n\t\t\tthis.handlers.set(type, new Set());\n\t\t}\n\t\tconst handlersForType = this.handlers.get(type)!;\n\t\thandlersForType.add(handler);\n\n\t\t// Return unsubscribe function\n\t\treturn () => {\n\t\t\thandlersForType.delete(handler);\n\t\t\tif (handlersForType.size === 0) {\n\t\t\t\tthis.handlers.delete(type);\n\t\t\t}\n\t\t};\n\t}\n\n\tasync publish(events: ReadonlyArray<Evt>): Promise<void> {\n\t\tfor (const event of events) {\n\t\t\tconst handlersForType = this.handlers.get(event.type);\n\t\t\tif (handlersForType) {\n\t\t\t\t// Call all handlers for this event type\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tArray.from(handlersForType).map((handler) => handler(event)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n","import { err, ok, type Result } from \"../core/result\";\n\nexport type ValueObject<T> = Readonly<T>;\n\n/**\n * Deep freezes an object and all its nested properties recursively.\n * This ensures true immutability for value objects with nested structures.\n * Handles circular references by tracking visited objects.\n */\nfunction deepFreeze<T>(obj: T, visited = new WeakSet<object>()): Readonly<T> {\n\t// Handle null and non-objects\n\tif (obj === null || typeof obj !== \"object\") {\n\t\treturn obj as Readonly<T>;\n\t}\n\n\t// Handle circular references\n\tif (visited.has(obj as object)) {\n\t\treturn obj as Readonly<T>;\n\t}\n\n\t// Mark as visited\n\tvisited.add(obj as object);\n\n\t// Retrieve the property names defined on obj\n\tconst propNames = Object.getOwnPropertyNames(obj);\n\n\t// Freeze properties before freezing self\n\tfor (const name of propNames) {\n\t\tconst value = (obj as Record<string, unknown>)[name];\n\n\t\t// Freeze value if it is an object or array\n\t\tif (value && (typeof value === \"object\" || Array.isArray(value))) {\n\t\t\tdeepFreeze(value, visited);\n\t\t}\n\t}\n\n\treturn Object.freeze(obj) as Readonly<T>;\n}\n\n/**\n * Creates a deeply immutable value object from the given data.\n * All nested objects and arrays are frozen recursively.\n *\n * @param t - The data to convert into a value object\n * @returns A deeply frozen, immutable value object\n *\n * @example\n * ```typescript\n * const address = vo({\n * street: \"Main St\",\n * city: \"Berlin\",\n * coordinates: { lat: 52.5, lng: 13.4 }\n * });\n * // address.coordinates.lat = 99; // ❌ Error: Cannot assign to read-only property\n * ```\n */\nexport function vo<T>(t: T): ValueObject<T> {\n\treturn deepFreeze({ ...t });\n}\n\n/**\n * Compares two value objects for equality based on their values.\n * Uses deep equality comparison by serializing both objects to JSON.\n *\n * Note: This is a simple implementation. For production use with complex objects,\n * consider using a more robust deep equality library or implementing custom equality logic.\n *\n * @param a - First value object\n * @param b - Second value object\n * @returns true if both objects have the same values, false otherwise\n *\n * @example\n * ```typescript\n * const money1 = vo({ amount: 100, currency: \"USD\" });\n * const money2 = vo({ amount: 100, currency: \"USD\" });\n * voEquals(money1, money2); // true\n * ```\n */\nexport function voEquals<T>(a: ValueObject<T>, b: ValueObject<T>): boolean {\n\treturn JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * Creates a value object with optional validation.\n * Returns a Result type instead of throwing an error.\n *\n * @param t - The data to convert into a value object\n * @param validate - Validation function that returns true if valid\n * @param errorMessage - Optional custom error message if validation fails\n * @returns Result containing the value object if valid, or an error message if validation fails\n *\n * @example\n * ```typescript\n * const result = voWithValidation(\n * { amount: 100, currency: \"USD\" },\n * (m) => m.amount >= 0 && m.currency.length === 3,\n * \"Invalid money: amount must be non-negative and currency must be 3 characters\"\n * );\n *\n * if (result.ok) {\n * console.log(result.value); // Use the value object\n * } else {\n * console.error(result.error); // Handle validation error\n * }\n * ```\n */\nexport function voWithValidation<T>(\n\tt: T,\n\tvalidate: (value: T) => boolean,\n\terrorMessage?: string,\n): Result<ValueObject<T>, string> {\n\tif (!validate(t)) {\n\t\treturn err(\n\t\t\terrorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`,\n\t\t);\n\t}\n\treturn ok(vo(t));\n}\n\n/**\n * Creates a value object with optional validation.\n * Throws an error if validation fails.\n *\n * @param t - The data to convert into a value object\n * @param validate - Validation function that returns true if valid\n * @param errorMessage - Optional custom error message if validation fails\n * @returns A deeply frozen, immutable value object\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * const money = voWithValidationUnsafe(\n * { amount: 100, currency: \"USD\" },\n * (m) => m.amount >= 0 && m.currency.length === 3,\n * \"Invalid money: amount must be non-negative and currency must be 3 characters\"\n * );\n * ```\n */\nexport function voWithValidationUnsafe<T>(\n\tt: T,\n\tvalidate: (value: T) => boolean,\n\terrorMessage?: string,\n): ValueObject<T> {\n\tif (!validate(t)) {\n\t\tthrow new Error(\n\t\t\terrorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`,\n\t\t);\n\t}\n\treturn vo(t);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shirudo/ddd-kit",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Composable TypeScript toolkit for tactical DDD",
5
5
  "type": "module",
6
6
  "repository": {