@schorts/shared-kernel 1.0.4 โ 1.1.0
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/CHANGELOG +20 -1
- package/README.md +6 -0
- package/__tests__/auth/auth-provider.test.ts +6 -1
- package/__tests__/dao/dao.test.ts +109 -0
- package/__tests__/entities/entity-registry.test.ts +71 -0
- package/__tests__/entities/entity.test.ts +7 -7
- package/__tests__/unit-of-work/unit-of-work.test.ts +47 -0
- package/jest.config.js +2 -2
- package/package.json +10 -1
- package/src/auth/auth-provider.ts +2 -1
- package/src/dao/dao.ts +15 -0
- package/src/dao/index.ts +1 -0
- package/src/entities/entity-registry.ts +30 -0
- package/src/entities/entity.ts +5 -2
- package/src/entities/exceptions/entity-not-registered.ts +1 -0
- package/src/entities/exceptions/index.ts +1 -0
- package/src/entities/index.ts +2 -0
- package/src/unit-of-work/index.ts +1 -0
- package/src/unit-of-work/unit-of-work.ts +5 -0
package/CHANGELOG
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2025-09-30
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Now the `ValueObject` for ID property on `Entity` is required.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added `EntityRegistry` to dynamically instantiate entities in the DAO implementation.
|
|
17
|
+
|
|
18
|
+
## [1.0.5] - 2025-09-30
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Introduce `DAO` concept for a related package with a `Firestore` implementation.
|
|
23
|
+
- Introduce `UnitOfWOrk` concept for a related package with a `Firestore` implementation.
|
|
24
|
+
|
|
8
25
|
## [1.0.4] - 2025-09-30
|
|
9
26
|
|
|
10
27
|
### Fixed
|
|
@@ -21,5 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
38
|
|
|
22
39
|
- Removed indirect dependencies from the `package.json`.
|
|
23
40
|
|
|
24
|
-
[1.0.
|
|
41
|
+
[1.0.5]: https://github.com/schorts99/shared-kernel/compare/v1.0.5...v1.1.0
|
|
42
|
+
[1.0.5]: https://github.com/schorts99/shared-kernel/compare/v1.0.4...v0.0.5
|
|
43
|
+
[1.0.4]: https://github.com/schorts99/shared-kernel/compare/v1.0.3...v0.0.4
|
|
25
44
|
[1.0.3]: https://github.com/schorts99/shared-kernel/releases/tag/v1.0.3
|
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ npm install @schorts/shared-kernel --save
|
|
|
26
26
|
### ๐งฌ Entities
|
|
27
27
|
|
|
28
28
|
- **Entity:** Base class for identity-based domain entities.
|
|
29
|
+
- **EntityRegistry:** Dynamic registry for entity constructors, enabling polymorphic and type-safe instantiation of domain entities from serialized data.
|
|
29
30
|
|
|
30
31
|
### ๐งน Formatters
|
|
31
32
|
|
|
@@ -44,6 +45,11 @@ npm install @schorts/shared-kernel --save
|
|
|
44
45
|
|
|
45
46
|
- **BaseModel:** Base class for serializable, type-safe models.
|
|
46
47
|
|
|
48
|
+
### ๐ Persistence
|
|
49
|
+
|
|
50
|
+
- **DAO:** Generic interface defining data access operations for domain entities.
|
|
51
|
+
- **UnitOfWork:** Interface enabling transactional coordination across multiple persistence operations.
|
|
52
|
+
|
|
47
53
|
### ๐ง State Manager
|
|
48
54
|
|
|
49
55
|
- **StateManager:** Abstract reactive state manager with listener support.
|
|
@@ -2,13 +2,18 @@ import { expectTypeOf } from "expect-type";
|
|
|
2
2
|
|
|
3
3
|
import { AuthProvider } from "../../src/auth";
|
|
4
4
|
import { Entity } from "../../src/entities";
|
|
5
|
+
import { UUIDValue } from "../../src/value-objects";
|
|
5
6
|
|
|
6
7
|
type Model = {
|
|
7
8
|
id: string;
|
|
8
9
|
name: string;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
class
|
|
12
|
+
class IDValue extends UUIDValue {
|
|
13
|
+
attributeName = "ID";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class TestUser extends Entity<IDValue, Model> {
|
|
12
17
|
toPrimitives(): Model {
|
|
13
18
|
return {
|
|
14
19
|
id: this.id.toString(),
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { expectTypeOf } from "expect-type";
|
|
2
|
+
|
|
3
|
+
import { DAO } from "../../src/dao";
|
|
4
|
+
import { Entity } from "../../src/entities";
|
|
5
|
+
import { Criteria } from "../../src/criteria";
|
|
6
|
+
import { UUIDValue } from "../../src/value-objects";
|
|
7
|
+
|
|
8
|
+
type Model = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
class IDValue extends UUIDValue {
|
|
14
|
+
attributeName = "ID";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class ModelEntity extends Entity<IDValue, Model> {
|
|
18
|
+
toPrimitives(): Model {
|
|
19
|
+
throw new Error("Method not implemented.");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("DAO", () => {
|
|
24
|
+
describe('#getAll', () => {
|
|
25
|
+
it('should declare the method', () => {
|
|
26
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("getAll");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should no require any arguments', () => {
|
|
30
|
+
expectTypeOf<DAO<Model, ModelEntity>['getAll']>().parameters.toEqualTypeOf<[]>();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return a promise with the Entity', () => {
|
|
34
|
+
expectTypeOf<DAO<Model, ModelEntity>['getAll']>().returns.toEqualTypeOf<Promise<ModelEntity[]>>();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('#findByID', () => {
|
|
39
|
+
it('should declare the method', () => {
|
|
40
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("findByID");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should require the ID argument', () => {
|
|
44
|
+
expectTypeOf<DAO<Model, ModelEntity>['findByID']>().parameters.toEqualTypeOf<[ModelEntity["id"]["value"]]>();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return a promise with the Entity or null', () => {
|
|
48
|
+
expectTypeOf<DAO<Model, ModelEntity>['findByID']>().returns.toEqualTypeOf<Promise<ModelEntity | null>>();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('#findOneBy', () => {
|
|
53
|
+
it('should declare the method', () => {
|
|
54
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("findOneBy");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should receive the criteria', () => {
|
|
58
|
+
expectTypeOf<DAO<Model, ModelEntity>['findOneBy']>().parameters.toEqualTypeOf<[Criteria]>();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return a promise with the Entity or null', () => {
|
|
62
|
+
expectTypeOf<DAO<Model, ModelEntity>['findOneBy']>().returns.toEqualTypeOf<Promise<ModelEntity | null>>();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('#search', () => {
|
|
67
|
+
it('should declare the method', () => {
|
|
68
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("search");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should receive the criteria argument', () => {
|
|
72
|
+
expectTypeOf<DAO<Model, ModelEntity>['search']>().parameters.toEqualTypeOf<[Criteria]>();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should return a promise with an array of entities', () => {
|
|
76
|
+
expectTypeOf<DAO<Model, ModelEntity>['search']>().returns.toEqualTypeOf<Promise<ModelEntity[]>>();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('#create', () => {
|
|
81
|
+
it('should declare the method', () => {
|
|
82
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("create");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should return a promise with the created entity', () => {
|
|
86
|
+
expectTypeOf<DAO<Model, ModelEntity>['create']>().returns.toEqualTypeOf<Promise<ModelEntity>>();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('#update', () => {
|
|
91
|
+
it('should declare the method', () => {
|
|
92
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("update");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return a promise with the updated entity', () => {
|
|
96
|
+
expectTypeOf<DAO<Model, ModelEntity>['update']>().returns.toEqualTypeOf<Promise<ModelEntity>>();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('#delete', () => {
|
|
101
|
+
it('should declare the method', () => {
|
|
102
|
+
expectTypeOf<DAO<Model, ModelEntity>>().toHaveProperty("delete");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should return a promise with the deleted entity', () => {
|
|
106
|
+
expectTypeOf<DAO<Model, ModelEntity>['delete']>().returns.toEqualTypeOf<Promise<ModelEntity>>();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { EntityRegistry, Entity, EntityNotRegistered } from "../../src/entities";
|
|
2
|
+
import { UUIDValue } from "../../src/value-objects";
|
|
3
|
+
|
|
4
|
+
type MockModel = {
|
|
5
|
+
id: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
class IDValue extends UUIDValue {
|
|
9
|
+
attributeName = 'ID';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class MockEntity extends Entity<IDValue, MockModel> {
|
|
13
|
+
constructor(id: IDValue) {
|
|
14
|
+
super(id);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toPrimitives(): MockModel {
|
|
18
|
+
return {
|
|
19
|
+
id: this.id.value,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static fromPrimitives<MockModel>(model: MockModel): MockEntity {
|
|
24
|
+
return new MockEntity(
|
|
25
|
+
new IDValue(model["id"]),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("EntityRegistry", () => {
|
|
31
|
+
const type = "mock";
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
(EntityRegistry as any).registry.clear();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("registers an entity constructor", () => {
|
|
38
|
+
EntityRegistry.register(type, MockEntity);
|
|
39
|
+
|
|
40
|
+
const resolved = EntityRegistry.resolve(type);
|
|
41
|
+
|
|
42
|
+
expect(resolved).toBe(MockEntity);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("resolves a registered entity constructor", () => {
|
|
46
|
+
EntityRegistry.register(type, MockEntity);
|
|
47
|
+
|
|
48
|
+
const resolved = EntityRegistry.resolve(type);
|
|
49
|
+
|
|
50
|
+
expect(resolved?.fromPrimitives).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns null for unregistered types", () => {
|
|
54
|
+
const resolved = EntityRegistry.resolve("unknown");
|
|
55
|
+
|
|
56
|
+
expect(resolved).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("creates an entity from a registered constructor", () => {
|
|
60
|
+
EntityRegistry.register(type, MockEntity);
|
|
61
|
+
|
|
62
|
+
const entity = EntityRegistry.create(type, { id: '123', name: 'Jorge' });
|
|
63
|
+
|
|
64
|
+
expect(entity).toBeInstanceOf(MockEntity);
|
|
65
|
+
expect(entity.id.value).toEqual('123');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("throws EntityNotRegistered for unknown type", () => {
|
|
69
|
+
expect(() => EntityRegistry.create("unknown", { id: '123' })).toThrow(EntityNotRegistered);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -31,7 +31,7 @@ class IDValue extends UUIDValue {
|
|
|
31
31
|
readonly attributeName = "ID";
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
class TestEntity extends Entity<Model> {
|
|
34
|
+
class TestEntity extends Entity<IDValue, Model> {
|
|
35
35
|
toPrimitives(): Model {
|
|
36
36
|
return {
|
|
37
37
|
id: this.id.toString(),
|
|
@@ -42,12 +42,12 @@ class TestEntity extends Entity<Model> {
|
|
|
42
42
|
|
|
43
43
|
describe('Entity', () => {
|
|
44
44
|
it('should have a "id" property of type string', () => {
|
|
45
|
-
expectTypeOf<Entity<Model>>().toHaveProperty("id");
|
|
46
|
-
expectTypeOf<Entity<Model>["id"]>().toEqualTypeOf<
|
|
45
|
+
expectTypeOf<Entity<IDValue, Model>>().toHaveProperty("id");
|
|
46
|
+
expectTypeOf<Entity<IDValue, Model>["id"]>().toEqualTypeOf<IDValue>();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('should have a private "domainEvents" property of type Array<DomainEvent>', () => {
|
|
50
|
-
expectTypeOf<Entity<Model>["domainEvents"]>().toEqualTypeOf<DomainEvent[]>();
|
|
50
|
+
expectTypeOf<Entity<IDValue, Model>["domainEvents"]>().toEqualTypeOf<DomainEvent[]>();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
describe('#pullDomainEvents', () => {
|
|
@@ -107,8 +107,8 @@ describe('Entity', () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
it('should declare a "toPrimitives" method', () => {
|
|
110
|
-
expectTypeOf<Entity<Model>>().toHaveProperty('toPrimitives');
|
|
111
|
-
expectTypeOf<Entity<Model>['toPrimitives']>().toBeFunction();
|
|
112
|
-
expectTypeOf<Entity<Model>['toPrimitives']>().returns.toEqualTypeOf<Model>();
|
|
110
|
+
expectTypeOf<Entity<IDValue, Model>>().toHaveProperty('toPrimitives');
|
|
111
|
+
expectTypeOf<Entity<IDValue, Model>['toPrimitives']>().toBeFunction();
|
|
112
|
+
expectTypeOf<Entity<IDValue, Model>['toPrimitives']>().returns.toEqualTypeOf<Model>();
|
|
113
113
|
});
|
|
114
114
|
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { expectTypeOf } from "expect-type";
|
|
2
|
+
|
|
3
|
+
import { UnitOfWork } from "../../src/unit-of-work";
|
|
4
|
+
|
|
5
|
+
describe("UnitOfWork", () => {
|
|
6
|
+
describe('#begin', () => {
|
|
7
|
+
it('should declare the method', () => {
|
|
8
|
+
expectTypeOf<UnitOfWork>().toHaveProperty("begin");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should no require any arguments', () => {
|
|
12
|
+
expectTypeOf<UnitOfWork['begin']>().parameters.toEqualTypeOf<[]>();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return a promise', () => {
|
|
16
|
+
expectTypeOf<UnitOfWork['begin']>().returns.toEqualTypeOf<Promise<void>>();
|
|
17
|
+
});
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('#commit', () => {
|
|
21
|
+
it('should declare the method', () => {
|
|
22
|
+
expectTypeOf<UnitOfWork>().toHaveProperty("commit");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should no require any arguments', () => {
|
|
26
|
+
expectTypeOf<UnitOfWork['commit']>().parameters.toEqualTypeOf<[]>();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return a promise', () => {
|
|
30
|
+
expectTypeOf<UnitOfWork['commit']>().returns.toEqualTypeOf<Promise<void>>();
|
|
31
|
+
});
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('#rollback', () => {
|
|
35
|
+
it('should declare the method', () => {
|
|
36
|
+
expectTypeOf<UnitOfWork>().toHaveProperty("rollback");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should no require any arguments', () => {
|
|
40
|
+
expectTypeOf<UnitOfWork['rollback']>().parameters.toEqualTypeOf<[]>();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return a promise', () => {
|
|
44
|
+
expectTypeOf<UnitOfWork['rollback']>().returns.toEqualTypeOf<Promise<void>>();
|
|
45
|
+
});
|
|
46
|
+
})
|
|
47
|
+
});
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schorts/shared-kernel",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A modular, type-safe foundation for building expressive, maintainable applications. This package provides core abstractions for domain modeling, HTTP integration, authentication, state management, and more โ designed to be framework-agnostic and highly extensible.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"import": "./dist/criteria/index.js",
|
|
14
14
|
"types": "./dist/criteria/index.d.ts"
|
|
15
15
|
},
|
|
16
|
+
"./dao": {
|
|
17
|
+
"import": "./dist/dao/index.js",
|
|
18
|
+
"types": "./dist/dao/index.d.ts"
|
|
19
|
+
},
|
|
16
20
|
"./domain-events": {
|
|
17
21
|
"import": "./dist/domain-events/index.js",
|
|
18
22
|
"types": "./dist/domain-events/index.d.ts"
|
|
@@ -41,6 +45,10 @@
|
|
|
41
45
|
"import": "./dist/models/index.js",
|
|
42
46
|
"types": "./dist/models/index.d.ts"
|
|
43
47
|
},
|
|
48
|
+
"./unit-of-work": {
|
|
49
|
+
"import": "./dist/unit-of-work/index.js",
|
|
50
|
+
"types": "./dist/unit-of-work/index.d.ts"
|
|
51
|
+
},
|
|
44
52
|
"./utils": {
|
|
45
53
|
"import": "./dist/utils/index.js",
|
|
46
54
|
"types": "./dist/utils/index.d.ts"
|
|
@@ -81,6 +89,7 @@
|
|
|
81
89
|
"shared-kernel",
|
|
82
90
|
"auth",
|
|
83
91
|
"jsonapi",
|
|
92
|
+
"dao",
|
|
84
93
|
"kernel",
|
|
85
94
|
"value-objects",
|
|
86
95
|
"criteria",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Entity } from "../entities";
|
|
2
2
|
import { BaseModel } from "../models";
|
|
3
|
+
import { ValueObject } from "../value-objects";
|
|
3
4
|
|
|
4
|
-
export interface AuthProvider<UserEntity extends Entity<BaseModel>> {
|
|
5
|
+
export interface AuthProvider<UserEntity extends Entity<ValueObject, BaseModel>> {
|
|
5
6
|
authenticate(...args: any[]): Promise<void>;
|
|
6
7
|
logout(): Promise<void>;
|
|
7
8
|
isAuthenticated(): Promise<boolean>;
|
package/src/dao/dao.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseModel } from "../models";
|
|
2
|
+
import { Entity as BaseEntity } from "../entities";
|
|
3
|
+
import { Criteria } from "../criteria";
|
|
4
|
+
import { UnitOfWork } from "../unit-of-work";
|
|
5
|
+
import { ValueObject } from "../value-objects";
|
|
6
|
+
|
|
7
|
+
export interface DAO<Model extends BaseModel, Entity extends BaseEntity<ValueObject, Model>> {
|
|
8
|
+
getAll(): Promise<Entity[]>;
|
|
9
|
+
findByID(id: Entity["id"]["value"]): Promise<Entity | null>;
|
|
10
|
+
findOneBy(criteria: Criteria): Promise<Entity | null>;
|
|
11
|
+
search(criteria: Criteria): Promise<Entity[]>;
|
|
12
|
+
create(entity: Entity, uow?: UnitOfWork): Promise<Entity>;
|
|
13
|
+
update(entity: Entity, uow?: UnitOfWork): Promise<Entity>;
|
|
14
|
+
delete(entity: Entity, uow?: UnitOfWork): Promise<Entity>;
|
|
15
|
+
}
|
package/src/dao/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { DAO } from "./dao";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BaseModel } from "../models";
|
|
2
|
+
import { Entity } from "./entity";
|
|
3
|
+
import { EntityNotRegistered } from "./exceptions";
|
|
4
|
+
import { ValueObject } from "../value-objects";
|
|
5
|
+
|
|
6
|
+
type EntityConstructor<Model extends BaseModel = BaseModel> = {
|
|
7
|
+
fromPrimitives(model: Model): Entity<ValueObject, Model>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class EntityRegistry {
|
|
11
|
+
private static registry = new Map<string, EntityConstructor>();
|
|
12
|
+
|
|
13
|
+
static register<Model extends BaseModel>(type: string, entity: EntityConstructor<Model>): void {
|
|
14
|
+
this.registry.set(type, entity);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static resolve<Model extends BaseModel>(type: string): EntityConstructor<Model> | null {
|
|
18
|
+
return (this.registry.get(type) || null) as EntityConstructor<Model> | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static create<Model extends BaseModel>(type: string, model: Model): Entity<ValueObject, Model> {
|
|
22
|
+
const entity = this.resolve<Model>(type);
|
|
23
|
+
|
|
24
|
+
if (!entity) {
|
|
25
|
+
throw new EntityNotRegistered(`Entity type "${type}" not registered`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return entity.fromPrimitives(model);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/entities/entity.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { ValueObject } from "../value-objects";
|
|
|
2
2
|
import { BaseModel } from "../models";
|
|
3
3
|
import { DomainEvent } from "../domain-events";
|
|
4
4
|
|
|
5
|
-
export abstract class Entity<Model extends BaseModel> {
|
|
5
|
+
export abstract class Entity<IDValue extends ValueObject, Model extends BaseModel> {
|
|
6
6
|
private domainEvents: Array<DomainEvent> = [];
|
|
7
7
|
|
|
8
|
-
constructor(readonly id:
|
|
8
|
+
constructor(readonly id: IDValue) {}
|
|
9
9
|
|
|
10
10
|
pullDomainEvents(): Array<DomainEvent> {
|
|
11
11
|
const domainEvents = [...this.domainEvents];
|
|
@@ -19,4 +19,7 @@ export abstract class Entity<Model extends BaseModel> {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
abstract toPrimitives(): Model;
|
|
22
|
+
static fromPrimitives<Model extends BaseModel>(_model: Model) {
|
|
23
|
+
throw new Error("Method not implemented.");
|
|
24
|
+
}
|
|
22
25
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class EntityNotRegistered extends Error {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { EntityNotRegistered } from "./entity-not-registered";
|
package/src/entities/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { UnitOfWork } from "./unit-of-work";
|