@woltz/rich-domain 0.2.2 → 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.md +23 -75
- package/LICENSE +20 -20
- package/README.md +37 -20
- package/dist/base-entity.d.ts +2 -2
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +6 -4
- package/dist/base-entity.js.map +1 -1
- package/dist/criteria.d.ts +5 -11
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +4 -3
- package/dist/criteria.js.map +1 -1
- package/dist/deep-proxy.d.ts +3 -1
- package/dist/deep-proxy.d.ts.map +1 -1
- package/dist/deep-proxy.js +116 -29
- package/dist/deep-proxy.js.map +1 -1
- package/dist/domain-event-bus.d.ts +5 -6
- package/dist/domain-event-bus.d.ts.map +1 -1
- package/dist/domain-event-bus.js +3 -11
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.d.ts +1 -31
- package/dist/domain-event.d.ts.map +1 -1
- package/dist/domain-event.js +2 -1
- package/dist/domain-event.js.map +1 -1
- package/dist/entity.d.ts +2 -2
- package/dist/entity.js +1 -1
- package/dist/exceptions.d.ts +251 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +321 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/id.d.ts +3 -3
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +15 -4
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -8
- package/dist/index.js.map +1 -1
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +12 -1
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/index.d.ts +2 -39
- package/dist/repository/index.d.ts.map +1 -1
- package/dist/repository/index.js +2 -39
- package/dist/repository/index.js.map +1 -1
- package/dist/repository/unit-of-work.d.ts +0 -11
- package/dist/repository/unit-of-work.d.ts.map +1 -1
- package/dist/repository/unit-of-work.js +0 -35
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/criteria.d.ts +6 -2
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/criteria.js +1 -1
- package/dist/types/criteria.js.map +1 -1
- package/dist/types/domain-event.d.ts +32 -0
- package/dist/types/domain-event.d.ts.map +1 -0
- package/dist/types/domain-event.js +2 -0
- package/dist/types/domain-event.js.map +1 -0
- package/dist/types/domain.d.ts +2 -2
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/history-tracker.d.ts +1 -1
- package/dist/types/history-tracker.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +2 -5
- package/dist/value-object.js.map +1 -1
- package/eslint.config.js +3 -3
- package/jest.config.js +1 -1
- package/package.json +14 -20
- package/src/base-entity.ts +6 -5
- package/src/criteria.ts +11 -11
- package/src/deep-proxy.ts +447 -339
- package/src/domain-event-bus.ts +152 -166
- package/src/domain-event.ts +53 -90
- package/src/entity.ts +16 -16
- package/src/exceptions.ts +435 -0
- package/src/id.ts +107 -94
- package/src/index.ts +26 -9
- package/src/paginated-result.ts +14 -1
- package/src/repository/index.ts +2 -44
- package/src/repository/unit-of-work.ts +1 -44
- package/src/types/criteria.ts +7 -2
- package/src/types/domain-event.ts +38 -0
- package/src/types/domain.ts +2 -3
- package/src/types/history-tracker.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/validation-error.ts +97 -97
- package/src/value-object.ts +3 -6
- package/tests/criteria.test.ts +8 -0
- package/tests/domain-events.test.ts +431 -445
- package/tests/entity-validation.test.ts +2 -2
- package/tests/entity.test.ts +33 -33
- package/tests/history-tracker.spec.ts +57 -17
- package/tests/id.test.ts +341 -341
- package/tests/repository.test.ts +8 -4
- package/tests/to-json.test.ts +103 -91
- package/tests/utils.ts +254 -151
- package/tests/value-object-validation.test.ts +0 -9
- package/tests/value-objects.test.ts +52 -52
- package/tsconfig.json +2 -24
- package/.github/workflows/ci.yml +0 -40
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.vscode/settings.json +0 -3
- package/commitlint.config.js +0 -23
- package/dist/filtering.d.ts +0 -107
- package/dist/filtering.d.ts.map +0 -1
- package/dist/filtering.js +0 -202
- package/dist/filtering.js.map +0 -1
- package/dist/ordering.d.ts +0 -93
- package/dist/ordering.d.ts.map +0 -1
- package/dist/ordering.js +0 -154
- package/dist/ordering.js.map +0 -1
- package/dist/pagination.d.ts +0 -218
- package/dist/pagination.d.ts.map +0 -1
- package/dist/pagination.js +0 -281
- package/dist/pagination.js.map +0 -1
- package/dist/repository/in-memory-repository.d.ts +0 -50
- package/dist/repository/in-memory-repository.d.ts.map +0 -1
- package/dist/repository/in-memory-repository.js +0 -93
- package/dist/repository/in-memory-repository.js.map +0 -1
- package/dist/repository/mapper.d.ts +0 -56
- package/dist/repository/mapper.d.ts.map +0 -1
- package/dist/repository/mapper.js +0 -15
- package/dist/repository/mapper.js.map +0 -1
- package/dist/repository/types.d.ts +0 -87
- package/dist/repository/types.d.ts.map +0 -1
- package/dist/repository/types.js +0 -6
- package/dist/repository/types.js.map +0 -1
- package/dist/repository.d.ts +0 -2
- package/dist/repository.d.ts.map +0 -1
- package/dist/repository.js +0 -21
- package/dist/repository.js.map +0 -1
- package/dist/specification.d.ts +0 -102
- package/dist/specification.d.ts.map +0 -1
- package/dist/specification.js +0 -187
- package/dist/specification.js.map +0 -1
- package/dist/types/repository.d.ts +0 -43
- package/dist/types/repository.d.ts.map +0 -1
- package/dist/types/repository.js +0 -2
- package/dist/types/repository.js.map +0 -1
- package/dist/types.d.ts +0 -88
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -12
- package/dist/types.js.map +0 -1
- package/src/repository/in-memory-repository.ts +0 -116
package/tests/repository.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { Id, Aggregate, Criteria,
|
|
2
|
-
import {
|
|
1
|
+
import { Id, Aggregate, Criteria, PaginatedResult } from "../src";
|
|
2
|
+
import { Mapper } from "../src/repository";
|
|
3
3
|
import { Repository } from "../src/repository/base-repository";
|
|
4
|
+
import { BaseProps } from "../src/types";
|
|
5
|
+
import { InMemoryRepository } from "./utils";
|
|
4
6
|
|
|
5
7
|
// ============================================================================
|
|
6
8
|
// Test Domain Models
|
|
@@ -482,7 +484,7 @@ describe("Repository", () => {
|
|
|
482
484
|
});
|
|
483
485
|
|
|
484
486
|
it("should save new user (insert)", async () => {
|
|
485
|
-
expect(user.isNew).toBe(true);
|
|
487
|
+
expect(user.isNew()).toBe(true);
|
|
486
488
|
await repository.create(user);
|
|
487
489
|
|
|
488
490
|
const found = await repository.findById(user.id.value);
|
|
@@ -644,7 +646,9 @@ describe("Repository", () => {
|
|
|
644
646
|
}),
|
|
645
647
|
];
|
|
646
648
|
|
|
647
|
-
const persistenceList = domainList.map((d) =>
|
|
649
|
+
const persistenceList = domainList.map((d) =>
|
|
650
|
+
toPersistenceMapper.build(d)
|
|
651
|
+
);
|
|
648
652
|
|
|
649
653
|
expect(persistenceList).toHaveLength(2);
|
|
650
654
|
expect(persistenceList[0].id).toBe("1");
|
package/tests/to-json.test.ts
CHANGED
|
@@ -1,91 +1,103 @@
|
|
|
1
|
-
import { Id } from "../src";
|
|
2
|
-
import { Post, User, Address, Comment } from "./utils";
|
|
3
|
-
|
|
4
|
-
describe("toJson Functionality", () => {
|
|
5
|
-
it("should convert simple entity to JSON", () => {
|
|
6
|
-
const post = new Post({
|
|
7
|
-
id: new Id("1"),
|
|
8
|
-
title: "First Post",
|
|
9
|
-
content: "Hello World",
|
|
10
|
-
likes: 5,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const json = post.toJson();
|
|
14
|
-
expect(json).toEqual({
|
|
15
|
-
id: "1",
|
|
16
|
-
title: "First Post",
|
|
17
|
-
content: "Hello World",
|
|
18
|
-
likes: 5,
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should convert nested entities to JSON", () => {
|
|
23
|
-
const user = new User({
|
|
24
|
-
id: new Id("1"),
|
|
25
|
-
name: "John Doe",
|
|
26
|
-
email: "john@example.com",
|
|
27
|
-
posts: [
|
|
28
|
-
new Post({
|
|
29
|
-
id: new Id("1"),
|
|
30
|
-
title: "Post 1",
|
|
31
|
-
content: "Content 1",
|
|
32
|
-
likes: 0,
|
|
33
|
-
}),
|
|
34
|
-
new Post({
|
|
35
|
-
id: new Id("2"),
|
|
36
|
-
title: "Post 2",
|
|
37
|
-
content: "Content 2",
|
|
38
|
-
likes: 5,
|
|
39
|
-
}),
|
|
40
|
-
],
|
|
41
|
-
address: new Address({
|
|
42
|
-
street: "Main St",
|
|
43
|
-
city: "NYC",
|
|
44
|
-
zipCode: "10001",
|
|
45
|
-
}),
|
|
46
|
-
comments: [
|
|
47
|
-
new Comment({
|
|
48
|
-
id: new Id("1"),
|
|
49
|
-
text: "Great post!",
|
|
50
|
-
author: "Alice",
|
|
51
|
-
}),
|
|
52
|
-
],
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const json = user.toJson();
|
|
56
|
-
|
|
57
|
-
expect(json.id).toBe("1");
|
|
58
|
-
expect(json.name).toBe("John Doe");
|
|
59
|
-
expect(json.posts).toHaveLength(2);
|
|
60
|
-
expect(json.posts[0].title).toBe("Post 1");
|
|
61
|
-
expect(json.address.city).toBe("NYC");
|
|
62
|
-
expect(json.comments[0].text).toBe("Great post!");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("should handle deeply nested structures", () => {
|
|
66
|
-
const user = new User({
|
|
67
|
-
id: new Id("1"),
|
|
68
|
-
name: "John Doe",
|
|
69
|
-
email: "john@example.com",
|
|
70
|
-
posts: [
|
|
71
|
-
new Post({
|
|
72
|
-
id: new Id("1"),
|
|
73
|
-
title: "Post 1",
|
|
74
|
-
content: "Content 1",
|
|
75
|
-
likes: 0,
|
|
76
|
-
}),
|
|
77
|
-
],
|
|
78
|
-
address: new Address({
|
|
79
|
-
street: "Main St",
|
|
80
|
-
city: "NYC",
|
|
81
|
-
zipCode: "10001",
|
|
82
|
-
}),
|
|
83
|
-
comments: [],
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const json = user.toJson();
|
|
87
|
-
expect(typeof json).toBe("object");
|
|
88
|
-
expect(Array.isArray(json.posts)).toBe(true);
|
|
89
|
-
expect(json.posts[0].id).toBe("1");
|
|
90
|
-
});
|
|
91
|
-
|
|
1
|
+
import { Entity, Id } from "../src";
|
|
2
|
+
import { Post, User, Address, Comment } from "./utils";
|
|
3
|
+
|
|
4
|
+
describe("toJson Functionality", () => {
|
|
5
|
+
it("should convert simple entity to JSON", () => {
|
|
6
|
+
const post = new Post({
|
|
7
|
+
id: new Id("1"),
|
|
8
|
+
title: "First Post",
|
|
9
|
+
content: "Hello World",
|
|
10
|
+
likes: 5,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const json = post.toJson();
|
|
14
|
+
expect(json).toEqual({
|
|
15
|
+
id: "1",
|
|
16
|
+
title: "First Post",
|
|
17
|
+
content: "Hello World",
|
|
18
|
+
likes: 5,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should convert nested entities to JSON", () => {
|
|
23
|
+
const user = new User({
|
|
24
|
+
id: new Id("1"),
|
|
25
|
+
name: "John Doe",
|
|
26
|
+
email: "john@example.com",
|
|
27
|
+
posts: [
|
|
28
|
+
new Post({
|
|
29
|
+
id: new Id("1"),
|
|
30
|
+
title: "Post 1",
|
|
31
|
+
content: "Content 1",
|
|
32
|
+
likes: 0,
|
|
33
|
+
}),
|
|
34
|
+
new Post({
|
|
35
|
+
id: new Id("2"),
|
|
36
|
+
title: "Post 2",
|
|
37
|
+
content: "Content 2",
|
|
38
|
+
likes: 5,
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
address: new Address({
|
|
42
|
+
street: "Main St",
|
|
43
|
+
city: "NYC",
|
|
44
|
+
zipCode: "10001",
|
|
45
|
+
}),
|
|
46
|
+
comments: [
|
|
47
|
+
new Comment({
|
|
48
|
+
id: new Id("1"),
|
|
49
|
+
text: "Great post!",
|
|
50
|
+
author: "Alice",
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const json = user.toJson();
|
|
56
|
+
|
|
57
|
+
expect(json.id).toBe("1");
|
|
58
|
+
expect(json.name).toBe("John Doe");
|
|
59
|
+
expect(json.posts).toHaveLength(2);
|
|
60
|
+
expect(json.posts[0].title).toBe("Post 1");
|
|
61
|
+
expect(json.address.city).toBe("NYC");
|
|
62
|
+
expect(json.comments[0].text).toBe("Great post!");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should handle deeply nested structures", () => {
|
|
66
|
+
const user = new User({
|
|
67
|
+
id: new Id("1"),
|
|
68
|
+
name: "John Doe",
|
|
69
|
+
email: "john@example.com",
|
|
70
|
+
posts: [
|
|
71
|
+
new Post({
|
|
72
|
+
id: new Id("1"),
|
|
73
|
+
title: "Post 1",
|
|
74
|
+
content: "Content 1",
|
|
75
|
+
likes: 0,
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
address: new Address({
|
|
79
|
+
street: "Main St",
|
|
80
|
+
city: "NYC",
|
|
81
|
+
zipCode: "10001",
|
|
82
|
+
}),
|
|
83
|
+
comments: [],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const json = user.toJson();
|
|
87
|
+
expect(typeof json).toBe("object");
|
|
88
|
+
expect(Array.isArray(json.posts)).toBe(true);
|
|
89
|
+
expect(json.posts[0].id).toBe("1");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should serialize date correctly", () => {
|
|
93
|
+
class Test extends Entity<{ id: Id; createdAt: Date }> {}
|
|
94
|
+
|
|
95
|
+
const test = new Test({
|
|
96
|
+
id: new Id("1"),
|
|
97
|
+
createdAt: new Date(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const json = test.toJson();
|
|
101
|
+
expect(json.createdAt).toBe(test.props.createdAt.toISOString());
|
|
102
|
+
});
|
|
103
|
+
});
|
package/tests/utils.ts
CHANGED
|
@@ -1,151 +1,254 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Test Entities & Value Objects
|
|
3
|
-
// ============================================================================
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Test Entities & Value Objects
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Aggregate,
|
|
7
|
+
Criteria,
|
|
8
|
+
Entity,
|
|
9
|
+
Id,
|
|
10
|
+
Mapper,
|
|
11
|
+
PaginatedResult,
|
|
12
|
+
Repository,
|
|
13
|
+
UnitOfWork,
|
|
14
|
+
ValueObject,
|
|
15
|
+
} from "../src";
|
|
16
|
+
|
|
17
|
+
interface AddressProps {
|
|
18
|
+
street: string;
|
|
19
|
+
city: string;
|
|
20
|
+
zipCode: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class Address extends ValueObject<AddressProps> {
|
|
24
|
+
get street(): string {
|
|
25
|
+
return this.props.street;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get city(): string {
|
|
29
|
+
return this.props.city;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get zipCode(): string {
|
|
33
|
+
return this.props.zipCode;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PostProps {
|
|
38
|
+
id: Id;
|
|
39
|
+
title: string;
|
|
40
|
+
content: string;
|
|
41
|
+
likes: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class Post extends Entity<PostProps> {
|
|
45
|
+
get title(): string {
|
|
46
|
+
return this.props.title;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set title(value: string) {
|
|
50
|
+
this.props.title = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get content(): string {
|
|
54
|
+
return this.props.content;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
set content(value: string) {
|
|
58
|
+
this.props.content = value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get likes(): number {
|
|
62
|
+
return this.props.likes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
set likes(value: number) {
|
|
66
|
+
this.props.likes = value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface CommentProps {
|
|
71
|
+
id: Id;
|
|
72
|
+
text: string;
|
|
73
|
+
author: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class Comment extends Entity<CommentProps> {
|
|
77
|
+
get text(): string {
|
|
78
|
+
return this.props.text;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
set text(value: string) {
|
|
82
|
+
this.props.text = value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get author(): string {
|
|
86
|
+
return this.props.author;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface UserProps {
|
|
91
|
+
id: Id;
|
|
92
|
+
name: string;
|
|
93
|
+
email: string;
|
|
94
|
+
posts: Post[];
|
|
95
|
+
address: Address;
|
|
96
|
+
comments: Comment[];
|
|
97
|
+
extra?: {
|
|
98
|
+
age: number;
|
|
99
|
+
height: number;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class User extends Aggregate<UserProps> {
|
|
104
|
+
get name(): string {
|
|
105
|
+
return this.props.name;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
set name(value: string) {
|
|
109
|
+
this.props.name = value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get email(): string {
|
|
113
|
+
return this.props.email;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get posts(): Post[] {
|
|
117
|
+
return this.props.posts;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
set posts(value: Post[]) {
|
|
121
|
+
this.props.posts = value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get address(): Address {
|
|
125
|
+
return this.props.address;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
set address(value: Address) {
|
|
129
|
+
this.props.address = value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get comments(): Comment[] {
|
|
133
|
+
return this.props.comments;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
set comments(value: Comment[]) {
|
|
137
|
+
this.props.comments = value;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public addPost(post: Post) {
|
|
141
|
+
this.props.posts.push(post);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public addManyPosts(posts: Post[]) {
|
|
145
|
+
this.props.posts.push(...posts);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public removePostById(id: string) {
|
|
149
|
+
this.props.posts = this.props.posts.filter((post) => post.id.value !== id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public changeEmail(email: string) {
|
|
153
|
+
this.props.email = email;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public changeExtra(extra: { age: number; height: number }) {
|
|
157
|
+
this.props.extra = extra;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
class InMemoryRepository<
|
|
162
|
+
TDomain extends Aggregate<any>
|
|
163
|
+
> extends Repository<TDomain> {
|
|
164
|
+
protected items: Map<string, TDomain> = new Map();
|
|
165
|
+
readonly uow: UnitOfWork;
|
|
166
|
+
constructor(
|
|
167
|
+
protected readonly mapperToDomain: Mapper<unknown, TDomain>,
|
|
168
|
+
protected readonly mapperToPersistence: Mapper<TDomain, unknown>
|
|
169
|
+
) {
|
|
170
|
+
super();
|
|
171
|
+
this.uow = {} as UnitOfWork;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get model(): any {
|
|
175
|
+
// your database table name
|
|
176
|
+
return "inMemory";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async findById(id: string): Promise<TDomain | null> {
|
|
180
|
+
return this.items.get(id) || null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async find(criteria: Criteria<TDomain>): Promise<PaginatedResult<TDomain>> {
|
|
184
|
+
const allItems = Array.from(this.items.values());
|
|
185
|
+
return PaginatedResult.fromArray(allItems, criteria);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async findAll(criteria?: Criteria<TDomain>): Promise<TDomain[]> {
|
|
189
|
+
if (criteria) {
|
|
190
|
+
const result = await this.find(criteria);
|
|
191
|
+
return result.data;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return Array.from(this.items.values());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async findOne(criteria: Criteria<TDomain>): Promise<TDomain | null> {
|
|
198
|
+
const result = await this.find(criteria.clone().limit(1));
|
|
199
|
+
return result.data.length > 0 ? result.data[0] : null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async create(aggregate: TDomain): Promise<void> {
|
|
203
|
+
this.items.set(aggregate.id.value, aggregate);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async update(entity: TDomain): Promise<void> {
|
|
207
|
+
this.items.set(entity.id.value, entity);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async createMany(aggregates: TDomain[]): Promise<void> {
|
|
211
|
+
for (const aggregate of aggregates) {
|
|
212
|
+
await this.create(aggregate);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async delete(aggregate: TDomain): Promise<void> {
|
|
217
|
+
this.items.delete(aggregate.id.value);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async exists(id: string): Promise<boolean> {
|
|
221
|
+
return this.items.has(id);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async count(criteria?: Criteria<TDomain>): Promise<number> {
|
|
225
|
+
if (criteria) {
|
|
226
|
+
const result = await this.find(criteria);
|
|
227
|
+
return result.meta.total;
|
|
228
|
+
}
|
|
229
|
+
return this.items.size;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Clear all items (useful for test cleanup)
|
|
234
|
+
*/
|
|
235
|
+
clear(): void {
|
|
236
|
+
this.items.clear();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get all items as array (useful for debugging)
|
|
241
|
+
*/
|
|
242
|
+
getAll(): TDomain[] {
|
|
243
|
+
return Array.from(this.items.values());
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get items count
|
|
248
|
+
*/
|
|
249
|
+
size(): number {
|
|
250
|
+
return this.items.size;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export { User, Post, Comment, Address, InMemoryRepository };
|
|
@@ -54,9 +54,6 @@ class Money extends ValueObject<MoneyProps> {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
protected static hooks: VOHooks<MoneyProps, Money> = {
|
|
57
|
-
defaultValues: {
|
|
58
|
-
currency: "USD",
|
|
59
|
-
},
|
|
60
57
|
rules: (money) => {
|
|
61
58
|
if (money.amount > 1000000) {
|
|
62
59
|
throwValidationError("amount", "Amount cannot exceed 1,000,000");
|
|
@@ -135,12 +132,6 @@ describe("ValueObject with Validation", () => {
|
|
|
135
132
|
expect(money.currency).toBe("USD");
|
|
136
133
|
});
|
|
137
134
|
|
|
138
|
-
it("should apply default currency", () => {
|
|
139
|
-
const money = new Money({ amount: 50 } as any);
|
|
140
|
-
expect(money.amount).toBe(50);
|
|
141
|
-
expect(money.currency).toBe("USD");
|
|
142
|
-
});
|
|
143
|
-
|
|
144
135
|
it("should throw on negative amount", () => {
|
|
145
136
|
expect(() => {
|
|
146
137
|
new Money({ amount: -10, currency: "USD" });
|