@pocket-architect/core 0.1.18 → 0.1.20
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 +102 -0
- package/build/AggregateRoot.d.ts +0 -5
- package/build/AggregateRoot.js +1 -11
- package/build/AggregateRoot.js.map +1 -1
- package/build/DomainEvent.d.ts +4 -8
- package/build/DomainEvent.js +8 -4
- package/build/DomainEvent.js.map +1 -1
- package/build/Entity.d.ts +6 -1
- package/build/Entity.js +30 -0
- package/build/Entity.js.map +1 -1
- package/build/Entity.spec.js +33 -3
- package/build/Entity.spec.js.map +1 -1
- package/build/EntityId.d.ts +2 -2
- package/build/EntityId.js.map +1 -1
- package/build/EntityId.spec.js +6 -5
- package/build/EntityId.spec.js.map +1 -1
- package/build/EventBus.d.ts +2 -0
- package/build/EventBus.spec.d.ts +1 -0
- package/build/EventBus.spec.js +157 -0
- package/build/EventBus.spec.js.map +1 -0
- package/build/eventBus/InMemoryEventBus.d.ts +9 -2
- package/build/eventBus/InMemoryEventBus.js +43 -12
- package/build/eventBus/InMemoryEventBus.js.map +1 -1
- package/build/eventBus/InMemoryEventBus.spec.js +0 -3
- package/build/eventBus/InMemoryEventBus.spec.js.map +1 -1
- package/build/helpers.d.ts +1 -0
- package/build/helpers.js +30 -0
- package/build/helpers.js.map +1 -0
- package/build/helpers.spec.d.ts +1 -0
- package/build/helpers.spec.js +70 -0
- package/build/helpers.spec.js.map +1 -0
- package/build/index.d.ts +4 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/AggregateRoot.ts +0 -15
- package/src/DomainEvent.ts +10 -13
- package/src/Entity.spec.ts +28 -5
- package/src/Entity.ts +37 -2
- package/src/EntityId.spec.ts +5 -4
- package/src/EntityId.ts +3 -3
- package/src/EventBus.spec.ts +108 -0
- package/src/EventBus.ts +2 -0
- package/src/eventBus/InMemoryEventBus.spec.ts +1 -5
- package/src/eventBus/InMemoryEventBus.ts +30 -5
- package/src/helpers.spec.ts +62 -0
- package/src/helpers.ts +25 -0
- package/src/index.ts +7 -1
- package/src/AggregateRoot.spec.ts +0 -35
|
@@ -3,34 +3,65 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.InMemoryEventBus = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var events_1 = require("events");
|
|
6
|
-
var InMemoryEventBus = /** @class */ (function (
|
|
7
|
-
tslib_1.__extends(InMemoryEventBus, _super);
|
|
6
|
+
var InMemoryEventBus = /** @class */ (function () {
|
|
8
7
|
function InMemoryEventBus() {
|
|
9
|
-
|
|
8
|
+
this._events = [];
|
|
9
|
+
this._emitter = new events_1.EventEmitter();
|
|
10
10
|
}
|
|
11
|
+
InMemoryEventBus.prototype.push = function (event) {
|
|
12
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
13
|
+
return tslib_1.__generator(this, function (_a) {
|
|
14
|
+
this._events.push(event);
|
|
15
|
+
return [2 /*return*/];
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
InMemoryEventBus.prototype.clear = function () {
|
|
20
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
21
|
+
return tslib_1.__generator(this, function (_a) {
|
|
22
|
+
this._events = [];
|
|
23
|
+
return [2 /*return*/];
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
11
27
|
InMemoryEventBus.prototype.publish = function (events) {
|
|
28
|
+
if (events === void 0) { events = []; }
|
|
12
29
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
13
|
-
var _i,
|
|
30
|
+
var allEvents, _i, allEvents_1, event;
|
|
14
31
|
return tslib_1.__generator(this, function (_a) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
switch (_a.label) {
|
|
33
|
+
case 0:
|
|
34
|
+
allEvents = (this._events || []).concat(events !== null && events !== void 0 ? events : []);
|
|
35
|
+
for (_i = 0, allEvents_1 = allEvents; _i < allEvents_1.length; _i++) {
|
|
36
|
+
event = allEvents_1[_i];
|
|
37
|
+
this._emitter.emit(event.eventName, event);
|
|
38
|
+
}
|
|
39
|
+
return [4 /*yield*/, this.clear()];
|
|
40
|
+
case 1:
|
|
41
|
+
_a.sent();
|
|
42
|
+
return [2 /*return*/];
|
|
18
43
|
}
|
|
19
|
-
return [2 /*return*/];
|
|
20
44
|
});
|
|
21
45
|
});
|
|
22
46
|
};
|
|
47
|
+
InMemoryEventBus.prototype.on = function (event, listener) {
|
|
48
|
+
this._emitter.on(event, listener);
|
|
49
|
+
};
|
|
50
|
+
InMemoryEventBus.prototype.listenerCount = function (event) {
|
|
51
|
+
return this._emitter.listenerCount(event);
|
|
52
|
+
};
|
|
23
53
|
InMemoryEventBus.prototype.addSubscribers = function (subscribers) {
|
|
54
|
+
var _a;
|
|
24
55
|
for (var _i = 0, subscribers_1 = subscribers; _i < subscribers_1.length; _i++) {
|
|
25
56
|
var subscriber = subscribers_1[_i];
|
|
26
57
|
var events = subscriber.subscribedTo();
|
|
27
|
-
for (var
|
|
28
|
-
var event =
|
|
29
|
-
this.on(event.EVENT_NAME, subscriber.on.bind(subscriber));
|
|
58
|
+
for (var _b = 0, events_2 = events; _b < events_2.length; _b++) {
|
|
59
|
+
var event = events_2[_b];
|
|
60
|
+
this._emitter.on((_a = event.EVENT_NAME) !== null && _a !== void 0 ? _a : event.name, subscriber.on.bind(subscriber));
|
|
30
61
|
}
|
|
31
62
|
}
|
|
32
63
|
};
|
|
33
64
|
return InMemoryEventBus;
|
|
34
|
-
}(
|
|
65
|
+
}());
|
|
35
66
|
exports.InMemoryEventBus = InMemoryEventBus;
|
|
36
67
|
//# sourceMappingURL=InMemoryEventBus.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryEventBus.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.ts"],"names":[],"mappings":";;;;AAAA,iCAAsC;AAKtC;
|
|
1
|
+
{"version":3,"file":"InMemoryEventBus.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.ts"],"names":[],"mappings":";;;;AAAA,iCAAsC;AAKtC;IAIE;QAHU,YAAO,GAAqB,EAAE,CAAC;QAIvC,IAAI,CAAC,QAAQ,GAAG,IAAI,qBAAY,EAAE,CAAC;IACrC,CAAC;IAEK,+BAAI,GAAV,UAAW,KAAqB;;;gBAC9B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;;;KAC1B;IAEK,gCAAK,GAAX;;;gBACE,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;;;;KACnB;IAEK,kCAAO,GAAb,UAAc,MAA6B;QAA7B,uBAAA,EAAA,WAA6B;;;;;;wBACnC,SAAS,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,EAAE,CAAC,CAAC;wBAC5D,WAA6B,EAAT,uBAAS,EAAT,uBAAS,EAAT,IAAS,EAAE,CAAC;4BAArB,KAAK;4BACd,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;wBAC7C,CAAC;wBACD,qBAAM,IAAI,CAAC,KAAK,EAAE,EAAA;;wBAAlB,SAAkB,CAAC;;;;;KACpB;IAED,6BAAE,GAAF,UAAG,KAAa,EAAE,QAAoC;QACpD,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,wCAAa,GAAb,UAAc,KAAa;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,yCAAc,GAAd,UAAe,WAAoC;;QACjD,KAAyB,UAAW,EAAX,2BAAW,EAAX,yBAAW,EAAX,IAAW,EAAE,CAAC;YAAlC,IAAM,UAAU,oBAAA;YACnB,IAAM,MAAM,GAAG,UAAU,CAAC,YAAY,EAAE,CAAC;YACzC,KAAoB,UAAM,EAAN,iBAAM,EAAN,oBAAM,EAAN,IAAM,EAAE,CAAC;gBAAxB,IAAM,KAAK,eAAA;gBACd,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAA,KAAK,CAAC,UAAU,mCAAI,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IACH,uBAAC;AAAD,CAAC,AAxCD,IAwCC;AAxCY,4CAAgB"}
|
|
@@ -20,9 +20,6 @@ var TestEvent = /** @class */ (function (_super) {
|
|
|
20
20
|
function TestEvent() {
|
|
21
21
|
return _super !== null && _super.apply(this, arguments) || this;
|
|
22
22
|
}
|
|
23
|
-
TestEvent.prototype.toPrimitives = function () {
|
|
24
|
-
return {};
|
|
25
|
-
};
|
|
26
23
|
TestEvent.EVENT_NAME = 'TestEvent';
|
|
27
24
|
return TestEvent;
|
|
28
25
|
}(DomainEvent_1.DomainEvent));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryEventBus.spec.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.spec.ts"],"names":[],"mappings":";;;AAAA,uDAAoD;AACpD,8CAA6D;AAC7D,wCAAqC;AAErC,kDAA+C;AAI/C;IAA4B,yCAAuD;IAAnF;;IAIA,CAAC;IAHQ,oBAAM,GAAb,UAAc,KAAqB,EAAE,EAA2B;QAC9D,OAAO,IAAI,aAAa,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IACH,oBAAC;AAAD,CAAC,AAJD,CAA4B,6BAAa,GAIxC;AAED;IAAwB,
|
|
1
|
+
{"version":3,"file":"InMemoryEventBus.spec.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createSnapshot<T>(obj: T): T;
|
package/build/helpers.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSnapshot = void 0;
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
function createSnapshot(obj) {
|
|
6
|
+
if (!obj) {
|
|
7
|
+
return obj;
|
|
8
|
+
}
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
if (typeof obj.toSnapshot === 'function') {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
return obj.toSnapshot();
|
|
15
|
+
}
|
|
16
|
+
var copy = {};
|
|
17
|
+
for (var key in obj) {
|
|
18
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (typeof obj[key] !== 'object') {
|
|
22
|
+
copy[key] = obj[key];
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
copy[key] = createSnapshot(obj[key]);
|
|
26
|
+
}
|
|
27
|
+
return copy;
|
|
28
|
+
}
|
|
29
|
+
exports.createSnapshot = createSnapshot;
|
|
30
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":";;;AAAA,8DAA8D;AAC9D,SAAgB,cAAc,CAAI,GAAM;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,GAAG,CAAC;IACb,CAAC;IACD,6DAA6D;IAC7D,aAAa;IACb,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QACzC,6DAA6D;QAC7D,aAAa;QACb,OAAO,GAAG,CAAC,UAAU,EAAO,CAAC;IAC/B,CAAC;IACD,IAAM,IAAI,GAAK,EAAO,CAAC;IACvB,KAAK,IAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YACpD,SAAS;QACX,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAvBD,wCAuBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var tslib_1 = require("tslib");
|
|
4
|
+
var helpers_1 = require("./helpers");
|
|
5
|
+
var EntityId_1 = require("./EntityId");
|
|
6
|
+
var Entity_1 = require("./Entity");
|
|
7
|
+
var TestEntity = /** @class */ (function (_super) {
|
|
8
|
+
tslib_1.__extends(TestEntity, _super);
|
|
9
|
+
function TestEntity() {
|
|
10
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
11
|
+
}
|
|
12
|
+
TestEntity.create = function (props, id) {
|
|
13
|
+
return new TestEntity(props, id);
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(TestEntity.prototype, "name", {
|
|
16
|
+
set: function (name) {
|
|
17
|
+
this.props.name = name;
|
|
18
|
+
},
|
|
19
|
+
enumerable: false,
|
|
20
|
+
configurable: true
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(TestEntity.prototype, "testId", {
|
|
23
|
+
set: function (val) {
|
|
24
|
+
this.props.testId = val;
|
|
25
|
+
},
|
|
26
|
+
enumerable: false,
|
|
27
|
+
configurable: true
|
|
28
|
+
});
|
|
29
|
+
TestEntity.prototype.toSnapshot = function () {
|
|
30
|
+
return tslib_1.__assign(tslib_1.__assign({}, this.props), { testId: undefined });
|
|
31
|
+
};
|
|
32
|
+
return TestEntity;
|
|
33
|
+
}(Entity_1.Entity));
|
|
34
|
+
describe('helpers', function () {
|
|
35
|
+
test('createSnapshot', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
36
|
+
var original, snapshot, item, itemSnapshot;
|
|
37
|
+
return tslib_1.__generator(this, function (_a) {
|
|
38
|
+
original = {
|
|
39
|
+
a: 1,
|
|
40
|
+
b: 'test',
|
|
41
|
+
c: {
|
|
42
|
+
d: 2,
|
|
43
|
+
e: 'nested',
|
|
44
|
+
f: {
|
|
45
|
+
g: 3
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
id: new EntityId_1.EntityId('1'),
|
|
49
|
+
};
|
|
50
|
+
snapshot = (0, helpers_1.createSnapshot)(original);
|
|
51
|
+
expect(snapshot).toEqual(original);
|
|
52
|
+
expect(snapshot).not.toBe(original);
|
|
53
|
+
expect(snapshot.c).not.toBe(original.c);
|
|
54
|
+
expect(snapshot.c.f).not.toBe(original.c.f);
|
|
55
|
+
expect(snapshot.id).not.toBe(original.id);
|
|
56
|
+
expect(snapshot.id).toEqual(new EntityId_1.EntityId('1'));
|
|
57
|
+
item = TestEntity.create({
|
|
58
|
+
name: 'test',
|
|
59
|
+
testId: new EntityId_1.EntityId('2')
|
|
60
|
+
}, '2');
|
|
61
|
+
itemSnapshot = (0, helpers_1.createSnapshot)(item);
|
|
62
|
+
expect(itemSnapshot).toEqual({
|
|
63
|
+
name: 'test',
|
|
64
|
+
testId: undefined
|
|
65
|
+
});
|
|
66
|
+
return [2 /*return*/];
|
|
67
|
+
});
|
|
68
|
+
}); });
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=helpers.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.spec.js","sourceRoot":"","sources":["../src/helpers.spec.ts"],"names":[],"mappings":";;;AAAA,qCAAyC;AACzC,uCAAoC;AACpC,mCAAgC;AAMhC;IAAyB,sCAA2C;IAApE;;IAmBA,CAAC;IAlBQ,iBAAM,GAAb,UAAc,KAAgB,EAAE,EAA2B;QACzD,OAAO,IAAI,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,sBAAI,4BAAI;aAAR,UAAS,IAAY;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QACzB,CAAC;;;OAAA;IAED,sBAAI,8BAAM;aAAV,UAAW,GAAqB;YAC9B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;QAC1B,CAAC;;;OAAA;IAED,+BAAU,GAAV;QACE,OAAO,sCACF,IAAI,CAAC,KAAK,KACb,MAAM,EAAE,SAAS,GACT,CAAC;IACb,CAAC;IACH,iBAAC;AAAD,CAAC,AAnBD,CAAyB,eAAM,GAmB9B;AAED,QAAQ,CAAC,SAAS,EAAE;IAClB,IAAI,CAAC,gBAAgB,EAAE;;;YACf,QAAQ,GAAG;gBACf,CAAC,EAAE,CAAC;gBACJ,CAAC,EAAE,MAAM;gBACT,CAAC,EAAE;oBACD,CAAC,EAAE,CAAC;oBACJ,CAAC,EAAE,QAAQ;oBACX,CAAC,EAAE;wBACD,CAAC,EAAE,CAAC;qBACL;iBACF;gBACD,EAAE,EAAE,IAAI,mBAAQ,CAAS,GAAG,CAAC;aAC9B,CAAC;YACI,QAAQ,GAAG,IAAA,wBAAc,EAAC,QAAQ,CAAC,CAAC;YAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC1C,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,mBAAQ,CAAS,GAAG,CAAC,CAAC,CAAC;YAEjD,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC;gBAC7B,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,IAAI,mBAAQ,CAAC,GAAG,CAAC;aAC1B,EAAE,GAAG,CAAC,CAAC;YACF,YAAY,GAAG,IAAA,wBAAc,EAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;gBAC3B,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;;;SACJ,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/build/index.d.ts
CHANGED
|
@@ -3,4 +3,7 @@ import { EntityId } from './EntityId';
|
|
|
3
3
|
import { ValueObject } from './ValueObject';
|
|
4
4
|
import { AggregateRoot } from './AggregateRoot';
|
|
5
5
|
import { HashError } from './error/HashError';
|
|
6
|
-
|
|
6
|
+
import { DomainEvent } from './DomainEvent';
|
|
7
|
+
import { DomainEventSubscriber } from './DomainEventSubscriber';
|
|
8
|
+
import { EventBus } from './EventBus';
|
|
9
|
+
export { Entity, EntityId, ValueObject, AggregateRoot, HashError, DomainEvent, DomainEventSubscriber, EventBus };
|
package/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HashError = exports.AggregateRoot = exports.ValueObject = exports.EntityId = exports.Entity = void 0;
|
|
3
|
+
exports.DomainEvent = exports.HashError = exports.AggregateRoot = exports.ValueObject = exports.EntityId = exports.Entity = void 0;
|
|
4
4
|
var Entity_1 = require("./Entity");
|
|
5
5
|
Object.defineProperty(exports, "Entity", { enumerable: true, get: function () { return Entity_1.Entity; } });
|
|
6
6
|
var EntityId_1 = require("./EntityId");
|
|
@@ -11,4 +11,6 @@ var AggregateRoot_1 = require("./AggregateRoot");
|
|
|
11
11
|
Object.defineProperty(exports, "AggregateRoot", { enumerable: true, get: function () { return AggregateRoot_1.AggregateRoot; } });
|
|
12
12
|
var HashError_1 = require("./error/HashError");
|
|
13
13
|
Object.defineProperty(exports, "HashError", { enumerable: true, get: function () { return HashError_1.HashError; } });
|
|
14
|
+
var DomainEvent_1 = require("./DomainEvent");
|
|
15
|
+
Object.defineProperty(exports, "DomainEvent", { enumerable: true, get: function () { return DomainEvent_1.DomainEvent; } });
|
|
14
16
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAUhC,uFAVO,eAAM,OAUP;AATR,uCAAsC;AAUpC,yFAVO,mBAAQ,OAUP;AATV,6CAA4C;AAU1C,4FAVO,yBAAW,OAUP;AATb,iDAAgD;AAU9C,8FAVO,6BAAa,OAUP;AATf,+CAA8C;AAU5C,0FAVO,qBAAS,OAUP;AATX,6CAA4C;AAU1C,4FAVO,yBAAW,OAUP"}
|
package/package.json
CHANGED
package/src/AggregateRoot.ts
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
import { Entity } from './Entity';
|
|
2
2
|
import { EntityId } from './EntityId';
|
|
3
|
-
import {DomainEvent} from "./DomainEvent";
|
|
4
3
|
|
|
5
4
|
export abstract class AggregateRoot<T, E, H extends EntityId<E>> extends Entity<T, E, H> {
|
|
6
|
-
private _domainEvents: DomainEvent<E>[] = [];
|
|
7
|
-
|
|
8
|
-
pullDomainEvents(): DomainEvent<E>[] {
|
|
9
|
-
const domainEvents = this._domainEvents.slice();
|
|
10
|
-
this._domainEvents = [];
|
|
11
|
-
return domainEvents;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
pushDomainEvent(event: DomainEvent<E>): void {
|
|
15
|
-
this._domainEvents.push(event);
|
|
16
|
-
}
|
|
17
5
|
}
|
|
18
|
-
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
-
export type AnyAggregateRoot<T> = AggregateRoot<any, T, any>;
|
package/src/DomainEvent.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import {EntityId} from "./EntityId";
|
|
2
|
-
import {AnyAggregateRoot} from "./AggregateRoot";
|
|
3
2
|
|
|
4
3
|
export abstract class DomainEvent<H> {
|
|
5
4
|
static EVENT_NAME: string;
|
|
6
5
|
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
9
|
-
readonly eventId: EntityId<H>;
|
|
6
|
+
readonly props: H;
|
|
7
|
+
readonly eventId: EntityId<never>;
|
|
10
8
|
readonly occurredOn: Date;
|
|
11
9
|
readonly eventName: string;
|
|
12
10
|
|
|
13
|
-
constructor(
|
|
11
|
+
constructor(props: H, eventId?: EntityId<never>, occurredOn?: Date) {
|
|
12
|
+
this.props = props;
|
|
13
|
+
|
|
14
14
|
const event = <typeof DomainEvent>this.constructor;
|
|
15
|
-
this.eventName = event.EVENT_NAME;
|
|
16
|
-
this.eventId = eventId || new EntityId<
|
|
17
|
-
this.aggregate = aggregate;
|
|
18
|
-
this.aggregateId = aggregate.id;
|
|
15
|
+
this.eventName = event.EVENT_NAME ?? this.constructor.name;
|
|
16
|
+
this.eventId = eventId || new EntityId<never>();
|
|
19
17
|
this.occurredOn = occurredOn || new Date();
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
toPrimitives(): H {
|
|
21
|
+
return this.props;
|
|
22
|
+
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -27,6 +27,3 @@ export type AnyDomainEvent = DomainEvent<any>;
|
|
|
27
27
|
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
29
|
export type DomainEventClass = typeof DomainEvent<any>;
|
|
30
|
-
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
-
type DomainEventAttributes = any;
|
package/src/Entity.spec.ts
CHANGED
|
@@ -3,29 +3,38 @@ import { EntityId } from './EntityId';
|
|
|
3
3
|
|
|
4
4
|
interface TestProps {
|
|
5
5
|
name: string;
|
|
6
|
+
testId?: EntityId<string>;
|
|
6
7
|
}
|
|
7
|
-
class TestEntity extends Entity<TestProps,
|
|
8
|
-
static create(props: TestProps, id?:EntityId<
|
|
8
|
+
class TestEntity extends Entity<TestProps, string, EntityId<string>> {
|
|
9
|
+
static create(props: TestProps, id?:EntityId<string>|string): TestEntity {
|
|
9
10
|
return new TestEntity(props, id);
|
|
10
11
|
}
|
|
12
|
+
|
|
13
|
+
set name(name: string) {
|
|
14
|
+
this.props.name = name;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set testId(val: EntityId<string>) {
|
|
18
|
+
this.props.testId = val;
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
describe('Entity', () => {
|
|
14
23
|
let entity: TestEntity;
|
|
15
24
|
|
|
16
25
|
beforeAll(() => {
|
|
17
|
-
entity = TestEntity.create({ name: 'test' }, 1);
|
|
26
|
+
entity = TestEntity.create({ name: 'test', testId: new EntityId('1') }, '1');
|
|
18
27
|
});
|
|
19
28
|
|
|
20
29
|
test('base', async () => {
|
|
21
30
|
const entityNew = TestEntity.create({ name: 'test2' });
|
|
22
31
|
expect(entityNew.id.toString().length).toEqual(36); // random id
|
|
23
32
|
|
|
24
|
-
const entity2 = TestEntity.create({ name: 'test2' }, 1);
|
|
33
|
+
const entity2 = TestEntity.create({ name: 'test2' }, '1');
|
|
25
34
|
expect(entity.equals(entity2)).toBeTruthy(); // same id
|
|
26
35
|
expect(entity.equals(entity)).toBeTruthy(); // same instance
|
|
27
36
|
expect(entity.equals(null)).toBeFalsy(); // null
|
|
28
|
-
expect(entity.equals(<TestEntity>{ id: new EntityId(1) })).toBeFalsy(); // not Entity
|
|
37
|
+
expect(entity.equals(<TestEntity>{ id: new EntityId('1') })).toBeFalsy(); // not Entity
|
|
29
38
|
expect(entity.id.toPrimitive()).toEqual('1');
|
|
30
39
|
expect(entity.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
|
|
31
40
|
|
|
@@ -34,4 +43,18 @@ describe('Entity', () => {
|
|
|
34
43
|
expect(entity3.id.toPrimitive()).toEqual('1');
|
|
35
44
|
expect(entity3.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
|
|
36
45
|
});
|
|
46
|
+
|
|
47
|
+
test('snapshot', async () => {
|
|
48
|
+
const snapshot = entity.snapshot();
|
|
49
|
+
expect(snapshot).toEqual({ name: 'test', testId: new EntityId('1') });
|
|
50
|
+
|
|
51
|
+
expect(() => {
|
|
52
|
+
snapshot.name = 'changed';
|
|
53
|
+
}).toThrow()
|
|
54
|
+
expect(entity.isDirty()).toEqual(false);
|
|
55
|
+
expect(entity.snapshotDiff()).toEqual(null);
|
|
56
|
+
entity.name = 'test changed';
|
|
57
|
+
expect(entity.isDirty()).toEqual(true);
|
|
58
|
+
expect(entity.snapshotDiff()).toEqual({"name": "test changed"});
|
|
59
|
+
});
|
|
37
60
|
});
|
package/src/Entity.ts
CHANGED
|
@@ -7,6 +7,35 @@ const isEntity = <T, E, M extends EntityId<E>>(v: Entity<T, E, M>): v is Entity<
|
|
|
7
7
|
export abstract class Entity<T, E, H extends EntityId<E>> {
|
|
8
8
|
protected readonly _id: H;
|
|
9
9
|
protected props: T;
|
|
10
|
+
protected _snapshot: string;
|
|
11
|
+
|
|
12
|
+
snapshot(): T {
|
|
13
|
+
this._snapshot = JSON.stringify(this.props);
|
|
14
|
+
const copy = JSON.parse(this._snapshot) as T;
|
|
15
|
+
Object.freeze(copy);
|
|
16
|
+
return copy;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
snapshotDiff(): Partial<T> | null {
|
|
20
|
+
if (!this._snapshot) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const diffs: Partial<T> = {};
|
|
24
|
+
const copy = JSON.parse(this._snapshot);
|
|
25
|
+
for (const key in this.props) {
|
|
26
|
+
if (JSON.stringify(this.props[key]) !== JSON.stringify(copy[key])) {
|
|
27
|
+
diffs[key] = this.props[key];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return Object.keys(diffs).length > 0 ? diffs : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
isDirty(): boolean {
|
|
34
|
+
if (!this._snapshot) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return this._snapshot !== JSON.stringify(this.props);
|
|
38
|
+
}
|
|
10
39
|
|
|
11
40
|
protected constructor(props: T, id?: H|E) {
|
|
12
41
|
if (id) {
|
|
@@ -41,7 +70,13 @@ export abstract class Entity<T, E, H extends EntityId<E>> {
|
|
|
41
70
|
return this.props
|
|
42
71
|
}
|
|
43
72
|
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
+
toJSON(): any {
|
|
75
|
+
return this.toPrimitive();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
toSnapshot(): any {
|
|
80
|
+
return this.toPrimitive();
|
|
46
81
|
}
|
|
47
82
|
}
|
package/src/EntityId.spec.ts
CHANGED
|
@@ -36,13 +36,14 @@ describe('ValueObject', () => {
|
|
|
36
36
|
test('toHash', async () => {
|
|
37
37
|
const id = new Test2Id('123');
|
|
38
38
|
expect(id.toHash()).toEqual('9KdMR');
|
|
39
|
-
|
|
40
|
-
expect(
|
|
41
|
-
expect(
|
|
39
|
+
const idFromHash = new Test2Id('VLoxm', true);
|
|
40
|
+
expect(idFromHash.toPrimitive()).toEqual('1');
|
|
41
|
+
expect(idFromHash.toPrimitive()).toEqual('1');
|
|
42
|
+
expect(idFromHash.toString()).toEqual('090bb5d1-267b-5967-84c8-95bc1bda380f');
|
|
42
43
|
|
|
43
44
|
const id2 = new TestId(123);
|
|
44
45
|
expect(id2.toHash()).toEqual('BJejP');
|
|
45
|
-
expect(
|
|
46
|
+
expect(new TestId('BJejP', true).toPrimitive()).toEqual('123');
|
|
46
47
|
|
|
47
48
|
const id3 = new Test2Id();
|
|
48
49
|
expect(() => id3.toHash()).toThrow('Cannot hash an empty recordId');
|
package/src/EntityId.ts
CHANGED
|
@@ -7,8 +7,8 @@ export class EntityId<T> {
|
|
|
7
7
|
protected _recordId: T|null = null;
|
|
8
8
|
protected _uuid: string = null;
|
|
9
9
|
|
|
10
|
-
constructor(recordId: T = null, isHash:boolean = false, uuid: string = null) {
|
|
11
|
-
this._recordId = recordId ? recordId : null;
|
|
10
|
+
constructor(recordId: T|string = null, isHash:boolean = false, uuid: string = null) {
|
|
11
|
+
this._recordId = recordId ? recordId as T : null;
|
|
12
12
|
if (isHash) {
|
|
13
13
|
this.fromHash(recordId as string);
|
|
14
14
|
}
|
|
@@ -33,7 +33,7 @@ export class EntityId<T> {
|
|
|
33
33
|
return hashids.encode(this.toPrimitive().toString());
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
fromHash(hash: string) : EntityId<T> {
|
|
36
|
+
protected fromHash(hash: string) : EntityId<T> {
|
|
37
37
|
if (!hash) {
|
|
38
38
|
return this;
|
|
39
39
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {InMemoryEventBus} from "./eventBus/InMemoryEventBus";
|
|
2
|
+
import {DomainEventSubscriber} from "./DomainEventSubscriber";
|
|
3
|
+
import {DomainEvent, DomainEventClass} from "./DomainEvent";
|
|
4
|
+
import {EntityId} from "./EntityId";
|
|
5
|
+
import {Entity} from "./Entity";
|
|
6
|
+
|
|
7
|
+
describe('EventBus', () => {
|
|
8
|
+
class NewUserCreated extends DomainEvent<{ username: string }> {
|
|
9
|
+
}
|
|
10
|
+
class NewUserCreated2 extends DomainEvent<{ username: string }> {
|
|
11
|
+
static EVENT_NAME = 'Event2';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class NewUserSubscriber implements DomainEventSubscriber {
|
|
15
|
+
subscribedTo(): DomainEventClass[] {
|
|
16
|
+
return [NewUserCreated, NewUserCreated2];
|
|
17
|
+
}
|
|
18
|
+
async on(domainEvent: NewUserCreated|NewUserCreated2): Promise<void> {
|
|
19
|
+
console.log('Event received:', domainEvent);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('lifecycle', async () => {
|
|
24
|
+
const newUserSubscriber = new NewUserSubscriber();
|
|
25
|
+
newUserSubscriber.on = jest.fn();
|
|
26
|
+
const hub = new InMemoryEventBus();
|
|
27
|
+
hub.addSubscribers([newUserSubscriber]);
|
|
28
|
+
|
|
29
|
+
const event = new NewUserCreated({ username: 'test' });
|
|
30
|
+
const event2 = new NewUserCreated2({ username: 'test2' });
|
|
31
|
+
|
|
32
|
+
expect(event.eventName).toEqual('NewUserCreated');
|
|
33
|
+
expect(event2.eventName).toEqual('Event2');
|
|
34
|
+
|
|
35
|
+
await hub.publish([event, event2]);
|
|
36
|
+
|
|
37
|
+
expect(newUserSubscriber.on).toHaveBeenCalledTimes(2);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('user example', async () => {
|
|
41
|
+
class UserId extends EntityId<string> {}
|
|
42
|
+
|
|
43
|
+
interface IUser {
|
|
44
|
+
id: UserId;
|
|
45
|
+
username: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class User extends Entity<IUser, string, UserId> {
|
|
49
|
+
static create(props: IUser): User {
|
|
50
|
+
return new User(props, props.id);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
set username(username: string) {
|
|
54
|
+
this.props.username = username;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get username(): string {
|
|
58
|
+
return this.props.username;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class UserCreatedEvent extends DomainEvent<Partial<IUser>> {
|
|
63
|
+
readonly eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
|
|
64
|
+
|
|
65
|
+
get username() {
|
|
66
|
+
return this.props.username;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class ChangeLoggerSubscriber implements DomainEventSubscriber {
|
|
71
|
+
// Підписуємося на події, які нас цікавлять
|
|
72
|
+
subscribedTo() {
|
|
73
|
+
return [UserCreatedEvent];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Реалізуємо логіку обробки подій
|
|
77
|
+
async on(domainEvent: UserCreatedEvent) {
|
|
78
|
+
console.log('Username changed:', domainEvent.username);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Далі створюємо EventBus і реєструємо підписника
|
|
83
|
+
const eventBus = new InMemoryEventBus();
|
|
84
|
+
const subscriber = new ChangeLoggerSubscriber();
|
|
85
|
+
const mockOn = jest.spyOn(subscriber, 'on');
|
|
86
|
+
eventBus.addSubscribers([subscriber]);
|
|
87
|
+
|
|
88
|
+
const user = User.create({ id: new UserId('1'), username: 'john_doe' });
|
|
89
|
+
// Створюємо знімок початкового стану
|
|
90
|
+
user.snapshot();
|
|
91
|
+
// Змінюємо ім'я користувача
|
|
92
|
+
user.username = 'john_doe_updated';
|
|
93
|
+
let event;
|
|
94
|
+
// Перевіряємо, чи є зміни
|
|
95
|
+
if (user.isDirty()) {
|
|
96
|
+
const diff = user.snapshotDiff();
|
|
97
|
+
if (diff) {
|
|
98
|
+
event = new UserCreatedEvent(diff);
|
|
99
|
+
// записуємо подію в чергу (не publish). Бо якщо в циклі може бути багато подій і їх треба назбирати, а потім виконати, тому збираємо через push
|
|
100
|
+
eventBus.push(event);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
expect(mockOn).not.toHaveBeenCalled();
|
|
104
|
+
// Публікуємо всі події в черзі
|
|
105
|
+
await eventBus.publish();
|
|
106
|
+
expect(mockOn).toHaveBeenCalledWith(event);
|
|
107
|
+
});
|
|
108
|
+
});
|
package/src/EventBus.ts
CHANGED
|
@@ -2,6 +2,8 @@ import {AnyDomainEvent} from './DomainEvent';
|
|
|
2
2
|
import {DomainEventSubscriber} from "./DomainEventSubscriber";
|
|
3
3
|
|
|
4
4
|
export interface EventBus {
|
|
5
|
+
push(event: AnyDomainEvent): Promise<void>;
|
|
6
|
+
clear(): Promise<void>;
|
|
5
7
|
publish(events: AnyDomainEvent[]): Promise<void>;
|
|
6
8
|
addSubscribers(subscribers: DomainEventSubscriber[]): void;
|
|
7
9
|
}
|
|
@@ -12,12 +12,8 @@ class TestAggregate extends AggregateRoot<ITestAggregate, number, EntityId<numbe
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
class TestEvent extends DomainEvent<
|
|
15
|
+
class TestEvent extends DomainEvent<TestAggregate> {
|
|
16
16
|
static EVENT_NAME = 'TestEvent';
|
|
17
|
-
|
|
18
|
-
toPrimitives() {
|
|
19
|
-
return {};
|
|
20
|
-
}
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
class TestSubscriber implements DomainEventSubscriber {
|