@shirudo/ddd-kit 1.0.0-rc.4 → 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/README.md +13 -16
- package/dist/index.d.ts +210 -157
- package/dist/index.js +71 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -571,26 +571,26 @@ var AggregateRoot = class extends Entity {
|
|
|
571
571
|
}
|
|
572
572
|
_config;
|
|
573
573
|
_autoVersionBump;
|
|
574
|
-
|
|
574
|
+
_pendingEvents = [];
|
|
575
575
|
/**
|
|
576
|
-
*
|
|
577
|
-
*
|
|
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
|
|
580
|
-
return Object.freeze(this.
|
|
579
|
+
get pendingEvents() {
|
|
580
|
+
return Object.freeze(this._pendingEvents.slice());
|
|
581
581
|
}
|
|
582
582
|
/**
|
|
583
|
-
* Clears the list
|
|
584
|
-
*
|
|
583
|
+
* Clears the pending-event list. Call this after the events have been
|
|
584
|
+
* dispatched (typically `markPersisted` handles it for you).
|
|
585
585
|
*/
|
|
586
|
-
|
|
587
|
-
this.
|
|
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
|
|
592
|
-
*
|
|
593
|
-
*
|
|
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.
|
|
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.
|
|
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
|
-
|
|
753
|
-
this.
|
|
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
|
};
|
|
@@ -765,8 +766,8 @@ var InfrastructureError = class extends BaseError {
|
|
|
765
766
|
}
|
|
766
767
|
};
|
|
767
768
|
var MissingHandlerError = class extends BaseError {
|
|
768
|
-
constructor(eventType) {
|
|
769
|
-
super(`Missing handler for event type: ${eventType}
|
|
769
|
+
constructor(eventType, cause) {
|
|
770
|
+
super(`Missing handler for event type: ${eventType}`, cause);
|
|
770
771
|
this.eventType = eventType;
|
|
771
772
|
}
|
|
772
773
|
static {
|
|
@@ -774,12 +775,12 @@ var MissingHandlerError = class extends BaseError {
|
|
|
774
775
|
}
|
|
775
776
|
};
|
|
776
777
|
var AggregateNotFoundError = class extends InfrastructureError {
|
|
777
|
-
constructor(aggregateType, id) {
|
|
778
|
-
super(`Aggregate not found: ${aggregateType}(${id})
|
|
778
|
+
constructor(aggregateType, id, cause) {
|
|
779
|
+
super(`Aggregate not found: ${aggregateType}(${id})`, cause);
|
|
779
780
|
this.aggregateType = aggregateType;
|
|
780
781
|
this.id = id;
|
|
781
782
|
this.withUserMessage(
|
|
782
|
-
`The requested ${aggregateType
|
|
783
|
+
`The requested ${aggregateType} could not be found.`
|
|
783
784
|
);
|
|
784
785
|
}
|
|
785
786
|
static {
|
|
@@ -787,9 +788,10 @@ var AggregateNotFoundError = class extends InfrastructureError {
|
|
|
787
788
|
}
|
|
788
789
|
};
|
|
789
790
|
var ConcurrencyConflictError = class extends InfrastructureError {
|
|
790
|
-
constructor(aggregateType, aggregateId, expectedVersion, actualVersion) {
|
|
791
|
+
constructor(aggregateType, aggregateId, expectedVersion, actualVersion, cause) {
|
|
791
792
|
super(
|
|
792
|
-
`Concurrency conflict on ${aggregateType}(${aggregateId}): expected version ${expectedVersion}, actual ${actualVersion}
|
|
793
|
+
`Concurrency conflict on ${aggregateType}(${aggregateId}): expected version ${expectedVersion}, actual ${actualVersion}`,
|
|
794
|
+
cause
|
|
793
795
|
);
|
|
794
796
|
this.aggregateType = aggregateType;
|
|
795
797
|
this.aggregateId = aggregateId;
|
|
@@ -825,7 +827,6 @@ var EventSourcedAggregate = class extends Entity {
|
|
|
825
827
|
}
|
|
826
828
|
// --- Event tracking ---
|
|
827
829
|
_pendingEvents = [];
|
|
828
|
-
_autoVersionBump;
|
|
829
830
|
get pendingEvents() {
|
|
830
831
|
return Object.freeze(this._pendingEvents.slice());
|
|
831
832
|
}
|
|
@@ -842,9 +843,8 @@ var EventSourcedAggregate = class extends Entity {
|
|
|
842
843
|
this.setVersion(version);
|
|
843
844
|
this._pendingEvents = [];
|
|
844
845
|
}
|
|
845
|
-
constructor(id, initialState
|
|
846
|
+
constructor(id, initialState) {
|
|
846
847
|
super(id, initialState);
|
|
847
|
-
this._autoVersionBump = config?.autoVersionBump ?? true;
|
|
848
848
|
}
|
|
849
849
|
// --- Event application ---
|
|
850
850
|
/**
|
|
@@ -892,18 +892,9 @@ var EventSourcedAggregate = class extends Entity {
|
|
|
892
892
|
this._state = freezeShallow(nextState);
|
|
893
893
|
if (isNew) {
|
|
894
894
|
this._pendingEvents.push(event);
|
|
895
|
-
|
|
896
|
-
this.setVersion(this._version + 1);
|
|
897
|
-
}
|
|
895
|
+
this.setVersion(this._version + 1);
|
|
898
896
|
}
|
|
899
897
|
}
|
|
900
|
-
/**
|
|
901
|
-
* Manually bumps the aggregate version.
|
|
902
|
-
* Only needed if `autoVersionBump` is disabled.
|
|
903
|
-
*/
|
|
904
|
-
bumpVersion() {
|
|
905
|
-
this.setVersion(this._version + 1);
|
|
906
|
-
}
|
|
907
898
|
// --- History & Snapshots ---
|
|
908
899
|
/**
|
|
909
900
|
* Reconstitutes the aggregate from an event history. Catches `DomainError`
|
|
@@ -929,15 +920,6 @@ var EventSourcedAggregate = class extends Entity {
|
|
|
929
920
|
this.setVersion(startVersion + history.length);
|
|
930
921
|
return ok();
|
|
931
922
|
}
|
|
932
|
-
hasPendingEvents() {
|
|
933
|
-
return this._pendingEvents.length > 0;
|
|
934
|
-
}
|
|
935
|
-
getEventCount() {
|
|
936
|
-
return this._pendingEvents.length;
|
|
937
|
-
}
|
|
938
|
-
getLatestEvent() {
|
|
939
|
-
return this._pendingEvents[this._pendingEvents.length - 1];
|
|
940
|
-
}
|
|
941
923
|
/**
|
|
942
924
|
* Creates a snapshot of the current aggregate state.
|
|
943
925
|
*/
|
|
@@ -961,7 +943,7 @@ var EventSourcedAggregate = class extends Entity {
|
|
|
961
943
|
restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
|
|
962
944
|
const previousState = this._state;
|
|
963
945
|
const previousVersion = this._version;
|
|
964
|
-
this._state = freezeShallow(snapshot.state);
|
|
946
|
+
this._state = freezeShallow(structuredClone(snapshot.state));
|
|
965
947
|
this.setVersion(snapshot.version);
|
|
966
948
|
for (const event of eventsAfterSnapshot) {
|
|
967
949
|
try {
|
|
@@ -981,10 +963,9 @@ var CommandBus = class {
|
|
|
981
963
|
static {
|
|
982
964
|
__name(this, "CommandBus");
|
|
983
965
|
}
|
|
984
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
985
966
|
handlers = /* @__PURE__ */ new Map();
|
|
986
967
|
register(commandType, handler) {
|
|
987
|
-
this.handlers.set(commandType, handler);
|
|
968
|
+
this.handlers.set(commandType, (cmd) => handler(cmd));
|
|
988
969
|
}
|
|
989
970
|
async execute(command) {
|
|
990
971
|
const handler = this.handlers.get(command.type);
|
|
@@ -1003,12 +984,22 @@ var CommandBus = class {
|
|
|
1003
984
|
|
|
1004
985
|
// src/app/handler.ts
|
|
1005
986
|
async function withCommit(deps, fn) {
|
|
1006
|
-
const { result, events } = await deps.scope.transactional(
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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 };
|
|
997
|
+
}
|
|
998
|
+
);
|
|
999
|
+
for (const agg of aggregates) {
|
|
1000
|
+
agg.markPersisted(agg.version);
|
|
1001
|
+
}
|
|
1002
|
+
if (deps.bus && events.length > 0) {
|
|
1012
1003
|
await deps.bus.publish(events);
|
|
1013
1004
|
}
|
|
1014
1005
|
return result;
|
|
@@ -1018,10 +1009,9 @@ var QueryBus = class {
|
|
|
1018
1009
|
static {
|
|
1019
1010
|
__name(this, "QueryBus");
|
|
1020
1011
|
}
|
|
1021
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1022
1012
|
handlers = /* @__PURE__ */ new Map();
|
|
1023
1013
|
register(queryType, handler) {
|
|
1024
|
-
this.handlers.set(queryType, handler);
|
|
1014
|
+
this.handlers.set(queryType, (query) => handler(query));
|
|
1025
1015
|
}
|
|
1026
1016
|
async execute(query) {
|
|
1027
1017
|
const handler = this.handlers.get(query.type);
|
|
@@ -1042,7 +1032,7 @@ var QueryBus = class {
|
|
|
1042
1032
|
if (!handler) {
|
|
1043
1033
|
throw new Error(`No handler registered for query type: ${query.type}`);
|
|
1044
1034
|
}
|
|
1045
|
-
return handler(query);
|
|
1035
|
+
return await handler(query);
|
|
1046
1036
|
}
|
|
1047
1037
|
};
|
|
1048
1038
|
|
|
@@ -1051,7 +1041,6 @@ var EventBusImpl = class {
|
|
|
1051
1041
|
static {
|
|
1052
1042
|
__name(this, "EventBusImpl");
|
|
1053
1043
|
}
|
|
1054
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1055
1044
|
handlers = /* @__PURE__ */ new Map();
|
|
1056
1045
|
subscribe(eventType, handler) {
|
|
1057
1046
|
const type = eventType;
|
|
@@ -1151,6 +1140,29 @@ var EventBusImpl = class {
|
|
|
1151
1140
|
}
|
|
1152
1141
|
};
|
|
1153
1142
|
|
|
1154
|
-
|
|
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 };
|
|
1155
1167
|
//# sourceMappingURL=index.js.map
|
|
1156
1168
|
//# sourceMappingURL=index.js.map
|