@shirudo/ddd-kit 1.0.0-rc.5 → 1.0.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -571,26 +571,26 @@ var AggregateRoot = class extends Entity {
571
571
  }
572
572
  _config;
573
573
  _autoVersionBump;
574
- _domainEvents = [];
574
+ _pendingEvents = [];
575
575
  /**
576
- * Returns a read-only list of domain events recorded by this aggregate.
577
- * These events are side-effects of state changes.
576
+ * Read-only list of domain events recorded on this aggregate that have
577
+ * not yet been flushed to the outbox / persistence layer.
578
578
  */
579
- get domainEvents() {
580
- return Object.freeze(this._domainEvents.slice());
579
+ get pendingEvents() {
580
+ return Object.freeze(this._pendingEvents.slice());
581
581
  }
582
582
  /**
583
- * Clears the list of recorded domain events.
584
- * Call this after dispatching the events.
583
+ * Clears the pending-event list. Call this after the events have been
584
+ * dispatched (typically `markPersisted` handles it for you).
585
585
  */
586
- clearDomainEvents() {
587
- this._domainEvents = [];
586
+ clearPendingEvents() {
587
+ this._pendingEvents = [];
588
588
  }
589
589
  /**
590
590
  * Post-save hook called by a `Repository.save()` implementation to push
591
- * the persisted version back into the in-memory aggregate and clear the
592
- * recorded domain events (they are now safely on the write side / in
593
- * the outbox).
591
+ * the persisted version back into the in-memory aggregate and clear
592
+ * pendingEvents (they are now safely on the write side / in the
593
+ * outbox).
594
594
  *
595
595
  * Use this so `save()` can keep its `Promise<void>` return type: the
596
596
  * caller holds the aggregate reference, which is up to date after this
@@ -598,7 +598,7 @@ var AggregateRoot = class extends Entity {
598
598
  */
599
599
  markPersisted(version) {
600
600
  this.setVersion(version);
601
- this._domainEvents = [];
601
+ this._pendingEvents = [];
602
602
  }
603
603
  /**
604
604
  * Mutates state and records the resulting domain events in the
@@ -687,7 +687,7 @@ var AggregateRoot = class extends Entity {
687
687
  * @param event - The domain event to record
688
688
  */
689
689
  addDomainEvent(event) {
690
- this._domainEvents.push(event);
690
+ this._pendingEvents.push(event);
691
691
  }
692
692
  /**
693
693
  * Manually bumps the aggregate version.
@@ -749,8 +749,9 @@ var AggregateRoot = class extends Entity {
749
749
  * ```
750
750
  */
751
751
  restoreFromSnapshot(snapshot) {
752
- this.validateState(snapshot.state);
753
- this._state = freezeShallow(snapshot.state);
752
+ const cloned = structuredClone(snapshot.state);
753
+ this.validateState(cloned);
754
+ this._state = freezeShallow(cloned);
754
755
  this.setVersion(snapshot.version);
755
756
  }
756
757
  };
@@ -826,7 +827,6 @@ var EventSourcedAggregate = class extends Entity {
826
827
  }
827
828
  // --- Event tracking ---
828
829
  _pendingEvents = [];
829
- _autoVersionBump;
830
830
  get pendingEvents() {
831
831
  return Object.freeze(this._pendingEvents.slice());
832
832
  }
@@ -843,9 +843,8 @@ var EventSourcedAggregate = class extends Entity {
843
843
  this.setVersion(version);
844
844
  this._pendingEvents = [];
845
845
  }
846
- constructor(id, initialState, config) {
846
+ constructor(id, initialState) {
847
847
  super(id, initialState);
848
- this._autoVersionBump = config?.autoVersionBump ?? true;
849
848
  }
850
849
  // --- Event application ---
851
850
  /**
@@ -893,18 +892,9 @@ var EventSourcedAggregate = class extends Entity {
893
892
  this._state = freezeShallow(nextState);
894
893
  if (isNew) {
895
894
  this._pendingEvents.push(event);
896
- if (this._autoVersionBump) {
897
- this.setVersion(this._version + 1);
898
- }
895
+ this.setVersion(this._version + 1);
899
896
  }
900
897
  }
901
- /**
902
- * Manually bumps the aggregate version.
903
- * Only needed if `autoVersionBump` is disabled.
904
- */
905
- bumpVersion() {
906
- this.setVersion(this._version + 1);
907
- }
908
898
  // --- History & Snapshots ---
909
899
  /**
910
900
  * Reconstitutes the aggregate from an event history. Catches `DomainError`
@@ -930,15 +920,6 @@ var EventSourcedAggregate = class extends Entity {
930
920
  this.setVersion(startVersion + history.length);
931
921
  return ok();
932
922
  }
933
- hasPendingEvents() {
934
- return this._pendingEvents.length > 0;
935
- }
936
- getEventCount() {
937
- return this._pendingEvents.length;
938
- }
939
- getLatestEvent() {
940
- return this._pendingEvents[this._pendingEvents.length - 1];
941
- }
942
923
  /**
943
924
  * Creates a snapshot of the current aggregate state.
944
925
  */
@@ -962,7 +943,7 @@ var EventSourcedAggregate = class extends Entity {
962
943
  restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
963
944
  const previousState = this._state;
964
945
  const previousVersion = this._version;
965
- this._state = freezeShallow(snapshot.state);
946
+ this._state = freezeShallow(structuredClone(snapshot.state));
966
947
  this.setVersion(snapshot.version);
967
948
  for (const event of eventsAfterSnapshot) {
968
949
  try {
@@ -982,10 +963,9 @@ var CommandBus = class {
982
963
  static {
983
964
  __name(this, "CommandBus");
984
965
  }
985
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
986
966
  handlers = /* @__PURE__ */ new Map();
987
967
  register(commandType, handler) {
988
- this.handlers.set(commandType, handler);
968
+ this.handlers.set(commandType, (cmd) => handler(cmd));
989
969
  }
990
970
  async execute(command) {
991
971
  const handler = this.handlers.get(command.type);
@@ -1004,13 +984,21 @@ var CommandBus = class {
1004
984
 
1005
985
  // src/app/handler.ts
1006
986
  async function withCommit(deps, fn) {
1007
- const { result, events } = await deps.scope.transactional(async (ctx) => {
1008
- const fnResult = await fn(ctx);
1009
- if (fnResult.events.length > 0) {
1010
- await deps.outbox.add(fnResult.events);
987
+ const { result, aggregates, events } = await deps.scope.transactional(
988
+ async (ctx) => {
989
+ const fnResult = await fn(ctx);
990
+ const harvested = fnResult.aggregates.flatMap(
991
+ (agg) => agg.pendingEvents
992
+ );
993
+ if (harvested.length > 0) {
994
+ await deps.outbox.add(harvested);
995
+ }
996
+ return { ...fnResult, events: harvested };
1011
997
  }
1012
- return fnResult;
1013
- });
998
+ );
999
+ for (const agg of aggregates) {
1000
+ agg.markPersisted(agg.version);
1001
+ }
1014
1002
  if (deps.bus && events.length > 0) {
1015
1003
  await deps.bus.publish(events);
1016
1004
  }
@@ -1021,10 +1009,9 @@ var QueryBus = class {
1021
1009
  static {
1022
1010
  __name(this, "QueryBus");
1023
1011
  }
1024
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1025
1012
  handlers = /* @__PURE__ */ new Map();
1026
1013
  register(queryType, handler) {
1027
- this.handlers.set(queryType, handler);
1014
+ this.handlers.set(queryType, (query) => handler(query));
1028
1015
  }
1029
1016
  async execute(query) {
1030
1017
  const handler = this.handlers.get(query.type);
@@ -1045,7 +1032,7 @@ var QueryBus = class {
1045
1032
  if (!handler) {
1046
1033
  throw new Error(`No handler registered for query type: ${query.type}`);
1047
1034
  }
1048
- return handler(query);
1035
+ return await handler(query);
1049
1036
  }
1050
1037
  };
1051
1038
 
@@ -1054,7 +1041,6 @@ var EventBusImpl = class {
1054
1041
  static {
1055
1042
  __name(this, "EventBusImpl");
1056
1043
  }
1057
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1058
1044
  handlers = /* @__PURE__ */ new Map();
1059
1045
  subscribe(eventType, handler) {
1060
1046
  const type = eventType;
@@ -1154,6 +1140,29 @@ var EventBusImpl = class {
1154
1140
  }
1155
1141
  };
1156
1142
 
1157
- export { AggregateNotFoundError, AggregateRoot, CommandBus, ConcurrencyConflictError, DomainError, Entity, EventBusImpl, EventSourcedAggregate, InfrastructureError, MissingHandlerError, QueryBus, ValueObject, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, withCommit };
1143
+ // src/events/outbox.ts
1144
+ var InMemoryOutbox = class {
1145
+ static {
1146
+ __name(this, "InMemoryOutbox");
1147
+ }
1148
+ pending = /* @__PURE__ */ new Map();
1149
+ async add(events) {
1150
+ for (const event of events) {
1151
+ this.pending.set(event.eventId, {
1152
+ dispatchId: event.eventId,
1153
+ event
1154
+ });
1155
+ }
1156
+ }
1157
+ async getPending(limit) {
1158
+ const all = [...this.pending.values()];
1159
+ return typeof limit === "number" ? all.slice(0, limit) : all;
1160
+ }
1161
+ async markDispatched(dispatchIds) {
1162
+ for (const id of dispatchIds) this.pending.delete(id);
1163
+ }
1164
+ };
1165
+
1166
+ export { AggregateNotFoundError, AggregateRoot, CommandBus, ConcurrencyConflictError, DomainError, Entity, EventBusImpl, EventSourcedAggregate, InMemoryOutbox, InfrastructureError, MissingHandlerError, QueryBus, ValueObject, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, withCommit };
1158
1167
  //# sourceMappingURL=index.js.map
1159
1168
  //# sourceMappingURL=index.js.map