@pocket-architect/core 0.1.18 → 0.1.19

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 ADDED
@@ -0,0 +1,102 @@
1
+ ## Entity
2
+
3
+ ### Snapshots
4
+
5
+ ```typescript
6
+ // Створює копію властивостей сутності у вигляді простого об'єкта.
7
+ // Також зберігає початковий стан для відстеження змін (dirty checking).
8
+ const snapshot = entity.snapshot();
9
+
10
+ entity.isDirty(); // Повертає true, якщо сутність була змінена після створення знімка.
11
+ entity.snapshotDiff(); // Повертає об'єкт з відмінностями між поточним станом сутності та знімком. Або null, якщо змін немає.
12
+ ```
13
+
14
+ ## Events
15
+
16
+ ### EventBus
17
+
18
+ Сутність для керування подіями в системі.
19
+ В монолітних застосунках зазвичай використовується один глобальний EventBus і його можна описати за допомогою InMemoryEventBus, не потрібно залучати чергу, бо всі обробники працюють в одному процесі.
20
+
21
+ ```typescript
22
+ // Спершу треба описати події, які є в домені. Для цього є клас DomainEvent, який треба наслідувати.
23
+ class UserCreatedEvent extends DomainEvent<{ userId: string; username: string }> {
24
+ readonly eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
25
+ }
26
+ class UserUpdatedEvent extends DomainEvent<{ userId: string; username: string }> {
27
+ readonly eventName = 'UserUpdatedEvent';
28
+ }
29
+
30
+ // Далі створюємо підписника на події, реалізуючи інтерфейс DomainEventSubscriber
31
+ class NewUserSubscriber implements DomainEventSubscriber {
32
+ // Підписуємося на події, які нас цікавлять
33
+ subscribedTo() {
34
+ return [UserCreatedEvent, UserUpdatedEvent];
35
+ }
36
+
37
+ // Реалізуємо логіку обробки подій
38
+ async on(domainEvent: UserCreatedEvent|UserUpdatedEvent) {
39
+ console.log('Event received:', domainEvent);
40
+ }
41
+ }
42
+
43
+ // Далі створюємо EventBus і реєструємо підписника
44
+ const eventBus = new InMemoryEventBus();
45
+ const subscriber = new NewUserSubscriber();
46
+ eventBus.addSubscribers([subscriber]);
47
+ const event = new UserCreatedEvent({ userId: '1', username: 'john_doe' });
48
+ const event2 = new UserUpdatedEvent({ userId: '1', username: 'john_doe_updated' });
49
+ // Публікуємо події
50
+ await eventBus.publish([event, event2]);
51
+ ```
52
+
53
+ У поєднанні snapshot та EventBus можна реалізувати відстеження змін в сутностях і публікації відповідних подій.
54
+
55
+ ```typescript
56
+ class UserId extends EntityId<string> {}
57
+
58
+ interface IUser {
59
+ id: UserId;
60
+ username: string;
61
+ }
62
+
63
+ class User extends Entity<IUser, string, UserId> {
64
+ }
65
+
66
+ class UserCreatedEvent extends DomainEvent<Partial<IUser>> {
67
+ readonly eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
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 NewUserSubscriber();
85
+ eventBus.addSubscribers([subscriber]);
86
+
87
+ const user = User.create({ id: new UserId('1'), username: 'john_doe' });
88
+ // Створюємо знімок початкового стану
89
+ user.snapshot();
90
+ // Змінюємо ім'я користувача
91
+ user.username = 'john_doe_updated';
92
+ // Перевіряємо, чи є зміни
93
+ if (user.isDirty()) {
94
+ const diff = user.snapshotDiff();
95
+ if (diff) {
96
+ // записуємо подію в чергу (не publish). Бо якщо в циклі може бути багато подій і їх треба назбирати, а потім виконати, тому збираємо через push
97
+ eventBus.push(new UserCreatedEvent(diff));
98
+ }
99
+ }
100
+ // Публікуємо всі події в черзі
101
+ await eventBus.publish();
102
+ ```
@@ -1,9 +1,4 @@
1
1
  import { Entity } from './Entity';
2
2
  import { EntityId } from './EntityId';
3
- import { DomainEvent } from "./DomainEvent";
4
3
  export declare abstract class AggregateRoot<T, E, H extends EntityId<E>> extends Entity<T, E, H> {
5
- private _domainEvents;
6
- pullDomainEvents(): DomainEvent<E>[];
7
- pushDomainEvent(event: DomainEvent<E>): void;
8
4
  }
9
- export type AnyAggregateRoot<T> = AggregateRoot<any, T, any>;
@@ -6,18 +6,8 @@ var Entity_1 = require("./Entity");
6
6
  var AggregateRoot = /** @class */ (function (_super) {
7
7
  tslib_1.__extends(AggregateRoot, _super);
8
8
  function AggregateRoot() {
9
- var _this = _super !== null && _super.apply(this, arguments) || this;
10
- _this._domainEvents = [];
11
- return _this;
9
+ return _super !== null && _super.apply(this, arguments) || this;
12
10
  }
13
- AggregateRoot.prototype.pullDomainEvents = function () {
14
- var domainEvents = this._domainEvents.slice();
15
- this._domainEvents = [];
16
- return domainEvents;
17
- };
18
- AggregateRoot.prototype.pushDomainEvent = function (event) {
19
- this._domainEvents.push(event);
20
- };
21
11
  return AggregateRoot;
22
12
  }(Entity_1.Entity));
23
13
  exports.AggregateRoot = AggregateRoot;
@@ -1 +1 @@
1
- {"version":3,"file":"AggregateRoot.js","sourceRoot":"","sources":["../src/AggregateRoot.ts"],"names":[],"mappings":";;;;AAAA,mCAAkC;AAIlC;IAAyE,yCAAe;IAAxF;;QACU,mBAAa,GAAqB,EAAE,CAAC;;IAW/C,CAAC;IATC,wCAAgB,GAAhB;QACE,IAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAChD,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,uCAAe,GAAf,UAAgB,KAAqB;QACnC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IACH,oBAAC;AAAD,CAAC,AAZD,CAAyE,eAAM,GAY9E;AAZqB,sCAAa"}
1
+ {"version":3,"file":"AggregateRoot.js","sourceRoot":"","sources":["../src/AggregateRoot.ts"],"names":[],"mappings":";;;;AAAA,mCAAkC;AAGlC;IAAyE,yCAAe;IAAxF;;IACA,CAAC;IAAD,oBAAC;AAAD,CAAC,AADD,CAAyE,eAAM,GAC9E;AADqB,sCAAa"}
@@ -1,16 +1,12 @@
1
1
  import { EntityId } from "./EntityId";
2
- import { AnyAggregateRoot } from "./AggregateRoot";
3
2
  export declare abstract class DomainEvent<H> {
4
3
  static EVENT_NAME: string;
5
- readonly aggregate: AnyAggregateRoot<H>;
6
- readonly aggregateId: H;
7
- readonly eventId: EntityId<H>;
4
+ readonly props: H;
5
+ readonly eventId: EntityId<never>;
8
6
  readonly occurredOn: Date;
9
7
  readonly eventName: string;
10
- constructor(aggregate: AnyAggregateRoot<H>, eventId?: EntityId<H>, occurredOn?: Date);
11
- abstract toPrimitives(): DomainEventAttributes;
8
+ constructor(props: H, eventId?: EntityId<never>, occurredOn?: Date);
9
+ toPrimitives(): H;
12
10
  }
13
11
  export type AnyDomainEvent = DomainEvent<any>;
14
12
  export type DomainEventClass = typeof DomainEvent<any>;
15
- type DomainEventAttributes = any;
16
- export {};
@@ -3,14 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DomainEvent = void 0;
4
4
  var EntityId_1 = require("./EntityId");
5
5
  var DomainEvent = /** @class */ (function () {
6
- function DomainEvent(aggregate, eventId, occurredOn) {
6
+ function DomainEvent(props, eventId, occurredOn) {
7
+ var _a;
8
+ this.props = props;
7
9
  var event = this.constructor;
8
- this.eventName = event.EVENT_NAME;
10
+ this.eventName = (_a = event.EVENT_NAME) !== null && _a !== void 0 ? _a : this.constructor.name;
9
11
  this.eventId = eventId || new EntityId_1.EntityId();
10
- this.aggregate = aggregate;
11
- this.aggregateId = aggregate.id;
12
12
  this.occurredOn = occurredOn || new Date();
13
13
  }
14
+ DomainEvent.prototype.toPrimitives = function () {
15
+ return this.props;
16
+ };
17
+ ;
14
18
  return DomainEvent;
15
19
  }());
16
20
  exports.DomainEvent = DomainEvent;
@@ -1 +1 @@
1
- {"version":3,"file":"DomainEvent.js","sourceRoot":"","sources":["../src/DomainEvent.ts"],"names":[],"mappings":";;;AAAA,uCAAoC;AAGpC;IASE,qBAAY,SAA8B,EAAE,OAAqB,EAAE,UAAiB;QAClF,IAAM,KAAK,GAAuB,IAAI,CAAC,WAAW,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,IAAI,mBAAQ,EAAK,CAAC;QAC5C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;IAC7C,CAAC;IAGH,kBAAC;AAAD,CAAC,AAnBD,IAmBC;AAnBqB,kCAAW"}
1
+ {"version":3,"file":"DomainEvent.js","sourceRoot":"","sources":["../src/DomainEvent.ts"],"names":[],"mappings":";;;AAAA,uCAAoC;AAEpC;IAQE,qBAAY,KAAQ,EAAE,OAAyB,EAAE,UAAiB;;QAChE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,IAAM,KAAK,GAAuB,IAAI,CAAC,WAAW,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,MAAA,KAAK,CAAC,UAAU,mCAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,IAAI,mBAAQ,EAAS,CAAC;QAChD,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,kCAAY,GAAZ;QACE,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAAA,CAAC;IACJ,kBAAC;AAAD,CAAC,AApBD,IAoBC;AApBqB,kCAAW"}
package/build/Entity.d.ts CHANGED
@@ -2,6 +2,10 @@ import { EntityId } from './EntityId';
2
2
  export declare abstract class Entity<T, E, H extends EntityId<E>> {
3
3
  protected readonly _id: H;
4
4
  protected props: T;
5
+ protected _snapshot: string;
6
+ snapshot(): T;
7
+ snapshotDiff(): Partial<T> | null;
8
+ isDirty(): boolean;
5
9
  protected constructor(props: T, id?: H | E);
6
10
  equals(object?: Entity<T, E, H>): boolean;
7
11
  get id(): H;
package/build/Entity.js CHANGED
@@ -15,6 +15,31 @@ var Entity = /** @class */ (function () {
15
15
  }
16
16
  this.props = props;
17
17
  }
18
+ Entity.prototype.snapshot = function () {
19
+ this._snapshot = JSON.stringify(this.props);
20
+ var copy = JSON.parse(this._snapshot);
21
+ Object.freeze(copy);
22
+ return copy;
23
+ };
24
+ Entity.prototype.snapshotDiff = function () {
25
+ if (!this._snapshot) {
26
+ return null;
27
+ }
28
+ var diffs = {};
29
+ var copy = JSON.parse(this._snapshot);
30
+ for (var key in this.props) {
31
+ if (JSON.stringify(this.props[key]) !== JSON.stringify(copy[key])) {
32
+ diffs[key] = this.props[key];
33
+ }
34
+ }
35
+ return Object.keys(diffs).length > 0 ? diffs : null;
36
+ };
37
+ Entity.prototype.isDirty = function () {
38
+ if (!this._snapshot) {
39
+ return false;
40
+ }
41
+ return this._snapshot !== JSON.stringify(this.props);
42
+ };
18
43
  Entity.prototype.equals = function (object) {
19
44
  if (object === null || object === undefined) {
20
45
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.js","sourceRoot":"","sources":["../src/Entity.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AAEtC,IAAM,QAAQ,GAAG,UAA8B,CAAkB;IAC/D,OAAO,CAAC,YAAY,MAAM,CAAA;AAC5B,CAAC,CAAA;AAED;IAIE,gBAAsB,KAAQ,EAAE,EAAQ;QACtC,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,GAAG,GAAM,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,mBAAQ,CAAI,EAAE,CAAC,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,GAAM,CAAC,IAAI,mBAAQ,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAEM,uBAAM,GAAb,UAAc,MAAwB;QACpC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,sBAAI,sBAAE;aAAN;YACE,OAAO,IAAI,CAAC,GAAG,CAAA;QACjB,CAAC;;;OAAA;IAED,4BAAW,GAAX;QACE,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,uBAAM,GAAN;QACE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAA;IAC3B,CAAC;IACH,aAAC;AAAD,CAAC,AAxCD,IAwCC;AAxCqB,wBAAM"}
1
+ {"version":3,"file":"Entity.js","sourceRoot":"","sources":["../src/Entity.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AAEtC,IAAM,QAAQ,GAAG,UAA8B,CAAkB;IAC/D,OAAO,CAAC,YAAY,MAAM,CAAA;AAC5B,CAAC,CAAA;AAED;IAiCE,gBAAsB,KAAQ,EAAE,EAAQ;QACtC,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,GAAG,GAAM,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,mBAAQ,CAAI,EAAE,CAAC,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,GAAM,CAAC,IAAI,mBAAQ,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAnCD,yBAAQ,GAAR;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAM,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6BAAY,GAAZ;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxC,KAAK,IAAM,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAClE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAED,wBAAO,GAAP;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IAWM,uBAAM,GAAb,UAAc,MAAwB;QACpC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,sBAAI,sBAAE;aAAN;YACE,OAAO,IAAI,CAAC,GAAG,CAAA;QACjB,CAAC;;;OAAA;IAED,4BAAW,GAAX;QACE,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,uBAAM,GAAN;QACE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAA;IAC3B,CAAC;IACH,aAAC;AAAD,CAAC,AArED,IAqEC;AArEqB,wBAAM"}
@@ -11,23 +11,30 @@ var TestEntity = /** @class */ (function (_super) {
11
11
  TestEntity.create = function (props, id) {
12
12
  return new TestEntity(props, id);
13
13
  };
14
+ Object.defineProperty(TestEntity.prototype, "name", {
15
+ set: function (name) {
16
+ this.props.name = name;
17
+ },
18
+ enumerable: false,
19
+ configurable: true
20
+ });
14
21
  return TestEntity;
15
22
  }(Entity_1.Entity));
16
23
  describe('Entity', function () {
17
24
  var entity;
18
25
  beforeAll(function () {
19
- entity = TestEntity.create({ name: 'test' }, 1);
26
+ entity = TestEntity.create({ name: 'test' }, '1');
20
27
  });
21
28
  test('base', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
22
29
  var entityNew, entity2, entity3;
23
30
  return tslib_1.__generator(this, function (_a) {
24
31
  entityNew = TestEntity.create({ name: 'test2' });
25
32
  expect(entityNew.id.toString().length).toEqual(36); // random id
26
- entity2 = TestEntity.create({ name: 'test2' }, 1);
33
+ entity2 = TestEntity.create({ name: 'test2' }, '1');
27
34
  expect(entity.equals(entity2)).toBeTruthy(); // same id
28
35
  expect(entity.equals(entity)).toBeTruthy(); // same instance
29
36
  expect(entity.equals(null)).toBeFalsy(); // null
30
- expect(entity.equals({ id: new EntityId_1.EntityId(1) })).toBeFalsy(); // not Entity
37
+ expect(entity.equals({ id: new EntityId_1.EntityId('1') })).toBeFalsy(); // not Entity
31
38
  expect(entity.id.toPrimitive()).toEqual('1');
32
39
  expect(entity.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
33
40
  entity3 = TestEntity.create({ name: 'test2' }, entity.id);
@@ -37,5 +44,21 @@ describe('Entity', function () {
37
44
  return [2 /*return*/];
38
45
  });
39
46
  }); });
47
+ test('snapshot', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
48
+ var snapshot;
49
+ return tslib_1.__generator(this, function (_a) {
50
+ snapshot = entity.snapshot();
51
+ expect(snapshot).toEqual({ name: 'test' });
52
+ expect(function () {
53
+ snapshot.name = 'changed';
54
+ }).toThrow();
55
+ expect(entity.isDirty()).toEqual(false);
56
+ expect(entity.snapshotDiff()).toEqual(null);
57
+ entity.name = 'test changed';
58
+ expect(entity.isDirty()).toEqual(true);
59
+ expect(entity.snapshotDiff()).toEqual({ "name": "test changed" });
60
+ return [2 /*return*/];
61
+ });
62
+ }); });
40
63
  });
41
64
  //# sourceMappingURL=Entity.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.spec.js","sourceRoot":"","sources":["../src/Entity.spec.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAClC,uCAAsC;AAKtC;IAAyB,sCAA2C;IAApE;;IAIA,CAAC;IAHQ,iBAAM,GAAb,UAAc,KAAgB,EAAE,EAA2B;QACzD,OAAO,IAAI,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IACH,iBAAC;AAAD,CAAC,AAJD,CAAyB,eAAM,GAI9B;AAED,QAAQ,CAAC,QAAQ,EAAE;IACjB,IAAI,MAAkB,CAAC;IAEvB,SAAS,CAAC;QACR,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,EAAE;;;YACL,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACvD,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY;YAE1D,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU;YACvD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,gBAAgB;YAC5D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO;YAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAa,EAAE,EAAE,EAAE,IAAI,mBAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,aAAa;YACrF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;YAEvE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU;YACvD,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;;;SAC/E,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Entity.spec.js","sourceRoot":"","sources":["../src/Entity.spec.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAClC,uCAAsC;AAKtC;IAAyB,sCAA2C;IAApE;;IAQA,CAAC;IAPQ,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;IACH,iBAAC;AAAD,CAAC,AARD,CAAyB,eAAM,GAQ9B;AAED,QAAQ,CAAC,QAAQ,EAAE;IACjB,IAAI,MAAkB,CAAC;IAEvB,SAAS,CAAC;QACR,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,EAAE;;;YACL,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACvD,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY;YAE1D,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;YAC1D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU;YACvD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,gBAAgB;YAC5D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO;YAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAa,EAAE,EAAE,EAAE,IAAI,mBAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,aAAa;YACvF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;YAEvE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU;YACvD,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;;;SAC/E,CAAC,CAAC;IAEH,IAAI,CAAC,UAAU,EAAE;;;YACT,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAE3C,MAAM,CAAC;gBACL,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAC;YAC5B,CAAC,CAAC,CAAC,OAAO,EAAE,CAAA;YACZ,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,CAAC,IAAI,GAAG,cAAc,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,cAAc,EAAC,CAAC,CAAC;;;SACjE,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,11 +1,11 @@
1
1
  export declare class EntityId<T> {
2
2
  protected _recordId: T | null;
3
3
  protected _uuid: string;
4
- constructor(recordId?: T, isHash?: boolean, uuid?: string);
4
+ constructor(recordId?: T | string, isHash?: boolean, uuid?: string);
5
5
  private createUUID;
6
6
  get hashOptions(): [string, number];
7
7
  toHash(): string;
8
- fromHash(hash: string): EntityId<T>;
8
+ protected fromHash(hash: string): EntityId<T>;
9
9
  toPrimitive(): T;
10
10
  toString(): string;
11
11
  equals(id: EntityId<T>): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"EntityId.js","sourceRoot":"","sources":["../src/EntityId.ts"],"names":[],"mappings":";;;;AAAA,0EAAuC;AACvC,8CAA+C;AAC/C,4DAA8B;AAC9B,+CAA4C;AAE5C;IAIE,kBAAY,QAAkB,EAAE,MAAsB,EAAE,IAAmB;QAA/D,yBAAA,EAAA,eAAkB;QAAE,uBAAA,EAAA,cAAsB;QAAE,qBAAA,EAAA,WAAmB;QAHjE,cAAS,GAAW,IAAI,CAAC;QACzB,UAAK,GAAW,IAAI,CAAC;QAG7B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,QAAkB,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,6BAAU,GAAlB;QACE,IAAI,CAAC,KAAK,GAAG,IAAA,wBAAU,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,gBAAQ,GAAE,EAAE,IAAA,wBAAU,EAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtH,CAAC;IAED,sBAAI,iCAAW;aAAf;YACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;;;OAAA;IAED,yBAAM,GAAN;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,2BAAQ,GAAR,UAAS,IAAY;QACnB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,IAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,qBAAS,CAAC,uBAAgB,IAAI,CAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,SAAS,GAAM,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAAW,GAAX;QACE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,2BAAQ,GAAR;QACE,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,yBAAM,GAAN,UAAO,EAAe;QACpB,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC;IACjC,CAAC;IACH,eAAC;AAAD,CAAC,AAvDD,IAuDC;AAvDY,4BAAQ"}
1
+ {"version":3,"file":"EntityId.js","sourceRoot":"","sources":["../src/EntityId.ts"],"names":[],"mappings":";;;;AAAA,0EAAuC;AACvC,8CAA+C;AAC/C,4DAA8B;AAC9B,+CAA4C;AAE5C;IAIE,kBAAY,QAAyB,EAAE,MAAsB,EAAE,IAAmB;QAAtE,yBAAA,EAAA,eAAyB;QAAE,uBAAA,EAAA,cAAsB;QAAE,qBAAA,EAAA,WAAmB;QAHxE,cAAS,GAAW,IAAI,CAAC;QACzB,UAAK,GAAW,IAAI,CAAC;QAG7B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QACjD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,QAAkB,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,6BAAU,GAAlB;QACE,IAAI,CAAC,KAAK,GAAG,IAAA,wBAAU,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,gBAAQ,GAAE,EAAE,IAAA,wBAAU,EAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtH,CAAC;IAED,sBAAI,iCAAW;aAAf;YACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;;;OAAA;IAED,yBAAM,GAAN;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAES,2BAAQ,GAAlB,UAAmB,IAAY;QAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,IAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,qBAAS,CAAC,uBAAgB,IAAI,CAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,SAAS,GAAM,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAAW,GAAX;QACE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,2BAAQ,GAAR;QACE,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,yBAAM,GAAN,UAAO,EAAe;QACpB,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC;IACjC,CAAC;IACH,eAAC;AAAD,CAAC,AAvDD,IAuDC;AAvDY,4BAAQ"}
@@ -49,16 +49,17 @@ describe('ValueObject', function () {
49
49
  });
50
50
  }); });
51
51
  test('toHash', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
52
- var id, id2, id3;
52
+ var id, idFromHash, id2, id3;
53
53
  return tslib_1.__generator(this, function (_a) {
54
54
  id = new Test2Id('123');
55
55
  expect(id.toHash()).toEqual('9KdMR');
56
- expect(id.fromHash('VLoxm').toPrimitive()).toEqual('1');
57
- expect(id.toPrimitive()).toEqual('1');
58
- expect(id.toString()).toEqual('090bb5d1-267b-5967-84c8-95bc1bda380f');
56
+ idFromHash = new Test2Id('VLoxm', true);
57
+ expect(idFromHash.toPrimitive()).toEqual('1');
58
+ expect(idFromHash.toPrimitive()).toEqual('1');
59
+ expect(idFromHash.toString()).toEqual('090bb5d1-267b-5967-84c8-95bc1bda380f');
59
60
  id2 = new TestId(123);
60
61
  expect(id2.toHash()).toEqual('BJejP');
61
- expect(id2.fromHash('BJejP').toPrimitive()).toEqual(123);
62
+ expect(new TestId('BJejP', true).toPrimitive()).toEqual('123');
62
63
  id3 = new Test2Id();
63
64
  expect(function () { return id3.toHash(); }).toThrow('Cannot hash an empty recordId');
64
65
  return [2 /*return*/];
@@ -1 +1 @@
1
- {"version":3,"file":"EntityId.spec.js","sourceRoot":"","sources":["../src/EntityId.spec.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AACtC,+CAA4C;AAE5C;IAAqB,kCAAgB;IAArC;;IACA,CAAC;IAAD,aAAC;AAAD,CAAC,AADD,CAAqB,mBAAQ,GAC5B;AAED;IAAsB,mCAAgB;IAAtC;;IACA,CAAC;IAAD,cAAC;AAAD,CAAC,AADD,CAAsB,mBAAQ,GAC7B;AAED,QAAQ,CAAC,aAAa,EAAE;IACtB,IAAI,CAAC,MAAM,EAAE;;;YACL,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,IAAI,mBAAQ,CAAC,GAAG,CAAC,CAAC;YACxB,GAAG,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEtB,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACvC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;;;SACzC,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,EAAE;;YACf,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAErB,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACnE,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAE9D,IAAI,CAAC;gBACH,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YAC1B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,sDAAsD;gBACtD,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,qBAAS,CAAC,CAAC;gBACtC,sDAAsD;gBACtD,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YAClD,CAAC;;;SACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,EAAE;;;YACP,EAAE,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACrC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxD,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;YAEhE,GAAG,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEnD,GAAG,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,CAAC,cAAM,OAAA,GAAG,CAAC,MAAM,EAAE,EAAZ,CAAY,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;;;SACrE,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"EntityId.spec.js","sourceRoot":"","sources":["../src/EntityId.spec.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { AnyDomainEvent } from './DomainEvent';
2
2
  import { DomainEventSubscriber } from "./DomainEventSubscriber";
3
3
  export interface EventBus {
4
+ push(event: AnyDomainEvent): Promise<void>;
5
+ clear(): Promise<void>;
4
6
  publish(events: AnyDomainEvent[]): Promise<void>;
5
7
  addSubscribers(subscribers: DomainEventSubscriber[]): void;
6
8
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var tslib_1 = require("tslib");
4
+ var InMemoryEventBus_1 = require("./eventBus/InMemoryEventBus");
5
+ var DomainEvent_1 = require("./DomainEvent");
6
+ var EntityId_1 = require("./EntityId");
7
+ var Entity_1 = require("./Entity");
8
+ describe('EventBus', function () {
9
+ var NewUserCreated = /** @class */ (function (_super) {
10
+ tslib_1.__extends(NewUserCreated, _super);
11
+ function NewUserCreated() {
12
+ return _super !== null && _super.apply(this, arguments) || this;
13
+ }
14
+ return NewUserCreated;
15
+ }(DomainEvent_1.DomainEvent));
16
+ var NewUserCreated2 = /** @class */ (function (_super) {
17
+ tslib_1.__extends(NewUserCreated2, _super);
18
+ function NewUserCreated2() {
19
+ return _super !== null && _super.apply(this, arguments) || this;
20
+ }
21
+ NewUserCreated2.EVENT_NAME = 'Event2';
22
+ return NewUserCreated2;
23
+ }(DomainEvent_1.DomainEvent));
24
+ var NewUserSubscriber = /** @class */ (function () {
25
+ function NewUserSubscriber() {
26
+ }
27
+ NewUserSubscriber.prototype.subscribedTo = function () {
28
+ return [NewUserCreated, NewUserCreated2];
29
+ };
30
+ NewUserSubscriber.prototype.on = function (domainEvent) {
31
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
32
+ return tslib_1.__generator(this, function (_a) {
33
+ console.log('Event received:', domainEvent);
34
+ return [2 /*return*/];
35
+ });
36
+ });
37
+ };
38
+ return NewUserSubscriber;
39
+ }());
40
+ test('lifecycle', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
41
+ var newUserSubscriber, hub, event, event2;
42
+ return tslib_1.__generator(this, function (_a) {
43
+ switch (_a.label) {
44
+ case 0:
45
+ newUserSubscriber = new NewUserSubscriber();
46
+ newUserSubscriber.on = jest.fn();
47
+ hub = new InMemoryEventBus_1.InMemoryEventBus();
48
+ hub.addSubscribers([newUserSubscriber]);
49
+ event = new NewUserCreated({ username: 'test' });
50
+ event2 = new NewUserCreated2({ username: 'test2' });
51
+ expect(event.eventName).toEqual('NewUserCreated');
52
+ expect(event2.eventName).toEqual('Event2');
53
+ return [4 /*yield*/, hub.publish([event, event2])];
54
+ case 1:
55
+ _a.sent();
56
+ expect(newUserSubscriber.on).toHaveBeenCalledTimes(2);
57
+ return [2 /*return*/];
58
+ }
59
+ });
60
+ }); });
61
+ test('user example', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
62
+ var UserId, User, UserCreatedEvent, ChangeLoggerSubscriber, eventBus, subscriber, mockOn, user, event, diff;
63
+ return tslib_1.__generator(this, function (_a) {
64
+ switch (_a.label) {
65
+ case 0:
66
+ UserId = /** @class */ (function (_super) {
67
+ tslib_1.__extends(UserId, _super);
68
+ function UserId() {
69
+ return _super !== null && _super.apply(this, arguments) || this;
70
+ }
71
+ return UserId;
72
+ }(EntityId_1.EntityId));
73
+ User = /** @class */ (function (_super) {
74
+ tslib_1.__extends(User, _super);
75
+ function User() {
76
+ return _super !== null && _super.apply(this, arguments) || this;
77
+ }
78
+ User.create = function (props) {
79
+ return new User(props, props.id);
80
+ };
81
+ Object.defineProperty(User.prototype, "username", {
82
+ get: function () {
83
+ return this.props.username;
84
+ },
85
+ set: function (username) {
86
+ this.props.username = username;
87
+ },
88
+ enumerable: false,
89
+ configurable: true
90
+ });
91
+ return User;
92
+ }(Entity_1.Entity));
93
+ UserCreatedEvent = /** @class */ (function (_super) {
94
+ tslib_1.__extends(UserCreatedEvent, _super);
95
+ function UserCreatedEvent() {
96
+ var _this = _super !== null && _super.apply(this, arguments) || this;
97
+ _this.eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
98
+ return _this;
99
+ }
100
+ Object.defineProperty(UserCreatedEvent.prototype, "username", {
101
+ get: function () {
102
+ return this.props.username;
103
+ },
104
+ enumerable: false,
105
+ configurable: true
106
+ });
107
+ return UserCreatedEvent;
108
+ }(DomainEvent_1.DomainEvent));
109
+ ChangeLoggerSubscriber = /** @class */ (function () {
110
+ function ChangeLoggerSubscriber() {
111
+ }
112
+ // Підписуємося на події, які нас цікавлять
113
+ ChangeLoggerSubscriber.prototype.subscribedTo = function () {
114
+ return [UserCreatedEvent];
115
+ };
116
+ // Реалізуємо логіку обробки подій
117
+ ChangeLoggerSubscriber.prototype.on = function (domainEvent) {
118
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
119
+ return tslib_1.__generator(this, function (_a) {
120
+ console.log('Username changed:', domainEvent.username);
121
+ return [2 /*return*/];
122
+ });
123
+ });
124
+ };
125
+ return ChangeLoggerSubscriber;
126
+ }());
127
+ eventBus = new InMemoryEventBus_1.InMemoryEventBus();
128
+ subscriber = new ChangeLoggerSubscriber();
129
+ mockOn = jest.spyOn(subscriber, 'on');
130
+ eventBus.addSubscribers([subscriber]);
131
+ user = User.create({ id: new UserId('1'), username: 'john_doe' });
132
+ // Створюємо знімок початкового стану
133
+ user.snapshot();
134
+ // Змінюємо ім'я користувача
135
+ user.username = 'john_doe_updated';
136
+ // Перевіряємо, чи є зміни
137
+ if (user.isDirty()) {
138
+ diff = user.snapshotDiff();
139
+ if (diff) {
140
+ event = new UserCreatedEvent(diff);
141
+ // записуємо подію в чергу (не publish). Бо якщо в циклі може бути багато подій і їх треба назбирати, а потім виконати, тому збираємо через push
142
+ eventBus.push(event);
143
+ }
144
+ }
145
+ expect(mockOn).not.toHaveBeenCalled();
146
+ // Публікуємо всі події в черзі
147
+ return [4 /*yield*/, eventBus.publish()];
148
+ case 1:
149
+ // Публікуємо всі події в черзі
150
+ _a.sent();
151
+ expect(mockOn).toHaveBeenCalledWith(event);
152
+ return [2 /*return*/];
153
+ }
154
+ });
155
+ }); });
156
+ });
157
+ //# sourceMappingURL=EventBus.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventBus.spec.js","sourceRoot":"","sources":["../src/EventBus.spec.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,9 @@ import { AnyDomainEvent } from '../DomainEvent';
4
4
  import { EventBus } from '../EventBus';
5
5
  import { DomainEventSubscriber } from "../DomainEventSubscriber";
6
6
  export declare class InMemoryEventBus extends EventEmitter implements EventBus {
7
- publish(events: AnyDomainEvent[]): Promise<void>;
7
+ protected _events: AnyDomainEvent[];
8
+ push(event: AnyDomainEvent): Promise<void>;
9
+ clear(): Promise<void>;
10
+ publish(events?: AnyDomainEvent[]): Promise<void>;
8
11
  addSubscribers(subscribers: DomainEventSubscriber[]): void;
9
12
  }
@@ -6,27 +6,54 @@ var events_1 = require("events");
6
6
  var InMemoryEventBus = /** @class */ (function (_super) {
7
7
  tslib_1.__extends(InMemoryEventBus, _super);
8
8
  function InMemoryEventBus() {
9
- return _super !== null && _super.apply(this, arguments) || this;
9
+ var _this = _super !== null && _super.apply(this, arguments) || this;
10
+ _this._events = [];
11
+ return _this;
10
12
  }
13
+ InMemoryEventBus.prototype.push = function (event) {
14
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
15
+ return tslib_1.__generator(this, function (_a) {
16
+ this._events.push(event);
17
+ return [2 /*return*/];
18
+ });
19
+ });
20
+ };
21
+ InMemoryEventBus.prototype.clear = function () {
22
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
23
+ return tslib_1.__generator(this, function (_a) {
24
+ this._events = [];
25
+ return [2 /*return*/];
26
+ });
27
+ });
28
+ };
11
29
  InMemoryEventBus.prototype.publish = function (events) {
30
+ if (events === void 0) { events = []; }
12
31
  return tslib_1.__awaiter(this, void 0, void 0, function () {
13
- var _i, events_2, event;
32
+ var allEvents, _i, allEvents_1, event;
14
33
  return tslib_1.__generator(this, function (_a) {
15
- for (_i = 0, events_2 = events; _i < events_2.length; _i++) {
16
- event = events_2[_i];
17
- this.emit(event.eventName, event);
34
+ switch (_a.label) {
35
+ case 0:
36
+ allEvents = (this._events || []).concat(events !== null && events !== void 0 ? events : []);
37
+ for (_i = 0, allEvents_1 = allEvents; _i < allEvents_1.length; _i++) {
38
+ event = allEvents_1[_i];
39
+ this.emit(event.eventName, event);
40
+ }
41
+ return [4 /*yield*/, this.clear()];
42
+ case 1:
43
+ _a.sent();
44
+ return [2 /*return*/];
18
45
  }
19
- return [2 /*return*/];
20
46
  });
21
47
  });
22
48
  };
23
49
  InMemoryEventBus.prototype.addSubscribers = function (subscribers) {
50
+ var _a;
24
51
  for (var _i = 0, subscribers_1 = subscribers; _i < subscribers_1.length; _i++) {
25
52
  var subscriber = subscribers_1[_i];
26
53
  var events = subscriber.subscribedTo();
27
- for (var _a = 0, events_3 = events; _a < events_3.length; _a++) {
28
- var event = events_3[_a];
29
- this.on(event.EVENT_NAME, subscriber.on.bind(subscriber));
54
+ for (var _b = 0, events_2 = events; _b < events_2.length; _b++) {
55
+ var event = events_2[_b];
56
+ this.on((_a = event.EVENT_NAME) !== null && _a !== void 0 ? _a : event.name, subscriber.on.bind(subscriber));
30
57
  }
31
58
  }
32
59
  };
@@ -1 +1 @@
1
- {"version":3,"file":"InMemoryEventBus.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.ts"],"names":[],"mappings":";;;;AAAA,iCAAsC;AAKtC;IAAsC,4CAAY;IAAlD;;IAeA,CAAC;IAdO,kCAAO,GAAb,UAAc,MAAwB;;;;gBACpC,WAA0B,EAAN,iBAAM,EAAN,oBAAM,EAAN,IAAM,EAAE,CAAC;oBAAlB,KAAK;oBACd,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;gBACpC,CAAC;;;;KACF;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,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IACH,uBAAC;AAAD,CAAC,AAfD,CAAsC,qBAAY,GAejD;AAfY,4CAAgB"}
1
+ {"version":3,"file":"InMemoryEventBus.js","sourceRoot":"","sources":["../../src/eventBus/InMemoryEventBus.ts"],"names":[],"mappings":";;;;AAAA,iCAAsC;AAKtC;IAAsC,4CAAY;IAAlD;;QACY,aAAO,GAAqB,EAAE,CAAC;;IA0B3C,CAAC;IAxBO,+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,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;wBACpC,CAAC;wBACD,qBAAM,IAAI,CAAC,KAAK,EAAE,EAAA;;wBAAlB,SAAkB,CAAC;;;;;KACpB;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,EAAE,CAAC,MAAA,KAAK,CAAC,UAAU,mCAAI,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IACH,uBAAC;AAAD,CAAC,AA3BD,CAAsC,qBAAY,GA2BjD;AA3BY,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":"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"}
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,qCAA0B;IAAlD;;IAEA,CAAC;IADQ,oBAAU,GAAG,WAAW,CAAC;IAClC,gBAAC;CAAA,AAFD,CAAwB,yBAAW,GAElC;AAED;IAAA;IASA,CAAC;IARC,qCAAY,GAAZ;QACE,OAAO,CAAC,SAAS,CAAC,CAAC;IACrB,CAAC;IAED,2BAAE,GAAF,UAAG,KAAe;QAChB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IACH,qBAAC;AAAD,CAAC,AATD,IASC;AAED,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;IAClB,OAAO;QACL,YAAY;YAAE;gBACZ,8DAA8D;gBAC9D,cAAS,GAAQ,EAAE,CAAC;YAetB,CAAC;YAbC,8DAA8D;YAC9D,oBAAE,GAAF,UAAG,KAAa,EAAE,QAAa;gBAC7B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YACnC,CAAC;YAED,8DAA8D;YAC9D,sBAAI,GAAJ,UAAK,KAAa;;gBAAE,cAAc;qBAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;oBAAd,6BAAc;;gBAChC,CAAA,KAAA,IAAI,CAAC,SAAS,CAAA,CAAC,KAAK,CAAC,WAAI,IAAI,EAAE;YACjC,CAAC;YAED,+BAAa,GAAb,UAAc,KAAa;gBACzB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,CAAC;YACH,cAAC;QAAD,CAAC,AAjBa,GAiBb;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE;IAC3B,IAAI,GAAqB,CAAC;IAC1B,IAAI,SAAwB,CAAC;IAE7B,UAAU,CAAC;QACT,GAAG,GAAG,IAAI,mCAAgB,EAAE,CAAC;QAC7B,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,mBAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE;;;;;oBACR,KAAK,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC;oBACjC,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;oBACvB,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;oBAC1B,qBAAM,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAA;;oBAA1B,SAA0B,CAAC;oBAC3B,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;;;;SAC1C,CAAC,CAAC;IAEH,IAAI,CAAC,gBAAgB,EAAE;;;;;oBACf,UAAU,GAAG,IAAI,cAAc,EAAE,CAAC;oBACxC,UAAU,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;oBAC1B,GAAG,CAAC,cAAc,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;oBAE3B,KAAK,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC;oBACvC,qBAAM,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAA;;oBAA1B,SAA0B,CAAC;oBAC3B,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAC/C,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;;;;SACnD,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pocket-architect/core",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -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>;
@@ -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 aggregate: AnyAggregateRoot<H>;
8
- readonly aggregateId: H;
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(aggregate: AnyAggregateRoot<H>, eventId?: EntityId<H>, occurredOn?: Date) {
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<H>();
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
- abstract toPrimitives(): DomainEventAttributes;
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;
@@ -4,28 +4,32 @@ import { EntityId } from './EntityId';
4
4
  interface TestProps {
5
5
  name: string;
6
6
  }
7
- class TestEntity extends Entity<TestProps, number, EntityId<number>> {
8
- static create(props: TestProps, id?:EntityId<number>|number): TestEntity {
7
+ class TestEntity extends Entity<TestProps, string, EntityId<string>> {
8
+ static create(props: TestProps, id?:EntityId<string>|string): TestEntity {
9
9
  return new TestEntity(props, id);
10
10
  }
11
+
12
+ set name(name: string) {
13
+ this.props.name = name;
14
+ }
11
15
  }
12
16
 
13
17
  describe('Entity', () => {
14
18
  let entity: TestEntity;
15
19
 
16
20
  beforeAll(() => {
17
- entity = TestEntity.create({ name: 'test' }, 1);
21
+ entity = TestEntity.create({ name: 'test' }, '1');
18
22
  });
19
23
 
20
24
  test('base', async () => {
21
25
  const entityNew = TestEntity.create({ name: 'test2' });
22
26
  expect(entityNew.id.toString().length).toEqual(36); // random id
23
27
 
24
- const entity2 = TestEntity.create({ name: 'test2' }, 1);
28
+ const entity2 = TestEntity.create({ name: 'test2' }, '1');
25
29
  expect(entity.equals(entity2)).toBeTruthy(); // same id
26
30
  expect(entity.equals(entity)).toBeTruthy(); // same instance
27
31
  expect(entity.equals(null)).toBeFalsy(); // null
28
- expect(entity.equals(<TestEntity>{ id: new EntityId(1) })).toBeFalsy(); // not Entity
32
+ expect(entity.equals(<TestEntity>{ id: new EntityId('1') })).toBeFalsy(); // not Entity
29
33
  expect(entity.id.toPrimitive()).toEqual('1');
30
34
  expect(entity.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
31
35
 
@@ -34,4 +38,18 @@ describe('Entity', () => {
34
38
  expect(entity3.id.toPrimitive()).toEqual('1');
35
39
  expect(entity3.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
36
40
  });
41
+
42
+ test('snapshot', async () => {
43
+ const snapshot = entity.snapshot();
44
+ expect(snapshot).toEqual({ name: 'test' });
45
+
46
+ expect(() => {
47
+ snapshot.name = 'changed';
48
+ }).toThrow()
49
+ expect(entity.isDirty()).toEqual(false);
50
+ expect(entity.snapshotDiff()).toEqual(null);
51
+ entity.name = 'test changed';
52
+ expect(entity.isDirty()).toEqual(true);
53
+ expect(entity.snapshotDiff()).toEqual({"name": "test changed"});
54
+ });
37
55
  });
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) {
@@ -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
- expect(id.fromHash('VLoxm').toPrimitive()).toEqual('1');
40
- expect(id.toPrimitive()).toEqual('1');
41
- expect(id.toString()).toEqual('090bb5d1-267b-5967-84c8-95bc1bda380f');
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(id2.fromHash('BJejP').toPrimitive()).toEqual(123);
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<number> {
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 {
@@ -4,17 +4,29 @@ import { EventBus } from '../EventBus';
4
4
  import {DomainEventSubscriber} from "../DomainEventSubscriber";
5
5
 
6
6
  export class InMemoryEventBus extends EventEmitter implements EventBus {
7
- async publish(events: AnyDomainEvent[]): Promise<void> {
8
- for (const event of events) {
7
+ protected _events: AnyDomainEvent[] = [];
8
+
9
+ async push(event: AnyDomainEvent): Promise<void> {
10
+ this._events.push(event);
11
+ }
12
+
13
+ async clear(): Promise<void> {
14
+ this._events = [];
15
+ }
16
+
17
+ async publish(events: AnyDomainEvent[] = []): Promise<void> {
18
+ const allEvents = (this._events || []).concat(events ?? []);
19
+ for (const event of allEvents) {
9
20
  this.emit(event.eventName, event);
10
21
  }
22
+ await this.clear();
11
23
  }
12
24
 
13
25
  addSubscribers(subscribers: DomainEventSubscriber[]): void {
14
26
  for (const subscriber of subscribers) {
15
27
  const events = subscriber.subscribedTo();
16
28
  for (const event of events) {
17
- this.on(event.EVENT_NAME, subscriber.on.bind(subscriber));
29
+ this.on(event.EVENT_NAME ?? event.name, subscriber.on.bind(subscriber));
18
30
  }
19
31
  }
20
32
  }
@@ -1,35 +0,0 @@
1
- import {AggregateRoot} from "./AggregateRoot";
2
- import {EntityId} from "./EntityId";
3
- import {DomainEvent} from "./DomainEvent";
4
-
5
- interface TestProps {
6
- name: string;
7
- }
8
- class TestEntity extends AggregateRoot<TestProps, number, EntityId<number>> {
9
- static create(props: TestProps, id?:EntityId<number>|number): TestEntity {
10
- return new TestEntity(props, id);
11
- }
12
- }
13
- class TestEvent extends DomainEvent<number> {
14
- readonly eventName = 'TestEvent';
15
-
16
- toPrimitives() {
17
- return {};
18
- }
19
- }
20
-
21
- describe('AggregateRoot', () => {
22
- let root: TestEntity;
23
-
24
- beforeAll(() => {
25
- root = TestEntity.create({ name: 'test' }, 1);
26
- });
27
-
28
- test('events', async () => {
29
- const event = new TestEvent(root);
30
- root.pushDomainEvent(event);
31
-
32
- expect(root.pullDomainEvents()).toEqual([event]);
33
- expect(root.pullDomainEvents()).toEqual([]);
34
- });
35
- });