@woltz/rich-domain 0.2.1 → 1.0.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +37 -20
  3. package/dist/base-entity.d.ts +1 -1
  4. package/dist/base-entity.d.ts.map +1 -1
  5. package/dist/base-entity.js +21 -15
  6. package/dist/base-entity.js.map +1 -1
  7. package/dist/constants.js +4 -1
  8. package/dist/constants.js.map +1 -1
  9. package/dist/criteria.d.ts.map +1 -1
  10. package/dist/criteria.js +7 -3
  11. package/dist/criteria.js.map +1 -1
  12. package/dist/deep-proxy.d.ts +3 -1
  13. package/dist/deep-proxy.d.ts.map +1 -1
  14. package/dist/deep-proxy.js +110 -33
  15. package/dist/deep-proxy.js.map +1 -1
  16. package/dist/domain-event-bus.js +7 -2
  17. package/dist/domain-event-bus.js.map +1 -1
  18. package/dist/domain-event.js +7 -3
  19. package/dist/domain-event.js.map +1 -1
  20. package/dist/entity.js +8 -3
  21. package/dist/entity.js.map +1 -1
  22. package/dist/id.d.ts +3 -3
  23. package/dist/id.d.ts.map +1 -1
  24. package/dist/id.js +10 -6
  25. package/dist/id.js.map +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +39 -16
  29. package/dist/index.js.map +1 -1
  30. package/dist/mapper.d.ts +4 -0
  31. package/dist/mapper.d.ts.map +1 -0
  32. package/dist/mapper.js +7 -0
  33. package/dist/mapper.js.map +1 -0
  34. package/dist/paginated-result.d.ts.map +1 -1
  35. package/dist/paginated-result.js +7 -6
  36. package/dist/paginated-result.js.map +1 -1
  37. package/dist/repository/base-repository.d.ts +25 -48
  38. package/dist/repository/base-repository.d.ts.map +1 -1
  39. package/dist/repository/base-repository.js +14 -51
  40. package/dist/repository/base-repository.js.map +1 -1
  41. package/dist/repository/in-memory-repository.d.ts +12 -8
  42. package/dist/repository/in-memory-repository.d.ts.map +1 -1
  43. package/dist/repository/in-memory-repository.js +24 -12
  44. package/dist/repository/in-memory-repository.js.map +1 -1
  45. package/dist/repository/index.d.ts +2 -39
  46. package/dist/repository/index.d.ts.map +1 -1
  47. package/dist/repository/index.js +26 -40
  48. package/dist/repository/index.js.map +1 -1
  49. package/dist/repository/unit-of-work.js +9 -3
  50. package/dist/repository/unit-of-work.js.map +1 -1
  51. package/dist/types/criteria.d.ts +2 -2
  52. package/dist/types/criteria.d.ts.map +1 -1
  53. package/dist/types/criteria.js +4 -1
  54. package/dist/types/criteria.js.map +1 -1
  55. package/dist/types/domain.js +2 -1
  56. package/dist/types/history-tracker.js +2 -1
  57. package/dist/types/index.d.ts +0 -1
  58. package/dist/types/index.d.ts.map +1 -1
  59. package/dist/types/index.js +22 -7
  60. package/dist/types/index.js.map +1 -1
  61. package/dist/types/standard-schema.js +2 -1
  62. package/dist/types/unit-of-work.d.ts +2 -2
  63. package/dist/types/unit-of-work.d.ts.map +1 -1
  64. package/dist/types/unit-of-work.js +2 -1
  65. package/dist/types/utils.js +2 -1
  66. package/dist/validation-error.js +9 -3
  67. package/dist/validation-error.js.map +1 -1
  68. package/dist/value-object.js +9 -5
  69. package/dist/value-object.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/base-entity.ts +3 -2
  72. package/src/criteria.ts +2 -2
  73. package/src/deep-proxy.ts +435 -339
  74. package/src/id.ts +4 -4
  75. package/src/index.ts +2 -3
  76. package/src/mapper.ts +3 -0
  77. package/src/paginated-result.ts +1 -8
  78. package/src/repository/base-repository.ts +27 -115
  79. package/src/repository/in-memory-repository.ts +28 -16
  80. package/src/repository/index.ts +2 -40
  81. package/src/types/criteria.ts +2 -2
  82. package/src/types/index.ts +0 -1
  83. package/src/types/unit-of-work.ts +3 -3
  84. package/tests/entity-validation.test.ts +1 -1
  85. package/tests/history-tracker.spec.ts +57 -17
  86. package/tests/id.test.ts +341 -341
  87. package/tests/repository.test.ts +95 -79
  88. package/tests/to-json.test.ts +103 -91
  89. package/tests/value-objects.test.ts +52 -52
  90. package/tsconfig.json +2 -2
  91. package/dist/filtering.d.ts +0 -107
  92. package/dist/filtering.d.ts.map +0 -1
  93. package/dist/filtering.js +0 -202
  94. package/dist/filtering.js.map +0 -1
  95. package/dist/ordering.d.ts +0 -93
  96. package/dist/ordering.d.ts.map +0 -1
  97. package/dist/ordering.js +0 -154
  98. package/dist/ordering.js.map +0 -1
  99. package/dist/pagination.d.ts +0 -218
  100. package/dist/pagination.d.ts.map +0 -1
  101. package/dist/pagination.js +0 -281
  102. package/dist/pagination.js.map +0 -1
  103. package/dist/repository/mapper.d.ts +0 -56
  104. package/dist/repository/mapper.d.ts.map +0 -1
  105. package/dist/repository/mapper.js +0 -15
  106. package/dist/repository/mapper.js.map +0 -1
  107. package/dist/repository/types.d.ts +0 -87
  108. package/dist/repository/types.d.ts.map +0 -1
  109. package/dist/repository/types.js +0 -6
  110. package/dist/repository/types.js.map +0 -1
  111. package/dist/repository.d.ts +0 -2
  112. package/dist/repository.d.ts.map +0 -1
  113. package/dist/repository.js +0 -21
  114. package/dist/repository.js.map +0 -1
  115. package/dist/specification.d.ts +0 -102
  116. package/dist/specification.d.ts.map +0 -1
  117. package/dist/specification.js +0 -187
  118. package/dist/specification.js.map +0 -1
  119. package/dist/types/repository.d.ts +0 -43
  120. package/dist/types/repository.d.ts.map +0 -1
  121. package/dist/types/repository.js +0 -2
  122. package/dist/types/repository.js.map +0 -1
  123. package/dist/types.d.ts +0 -88
  124. package/dist/types.d.ts.map +0 -1
  125. package/dist/types.js +0 -12
  126. package/dist/types.js.map +0 -1
  127. package/src/repository/mapper.ts +0 -74
  128. package/src/types/repository.ts +0 -51
@@ -1,9 +1,6 @@
1
1
  import { Id, Aggregate, Criteria, BaseProps, PaginatedResult } from "../src";
2
- import {
3
- InMemoryRepository,
4
- BaseRepository,
5
- BaseMapper,
6
- } from "../src/repository";
2
+ import { InMemoryRepository, Mapper } from "../src/repository";
3
+ import { Repository } from "../src/repository/base-repository";
7
4
 
8
5
  // ============================================================================
9
6
  // Test Domain Models
@@ -73,9 +70,8 @@ type UserPersistence = {
73
70
  // ============================================================================
74
71
  // Mapper Implementation
75
72
  // ============================================================================
76
-
77
- class UserMapper extends BaseMapper<User, UserPersistence> {
78
- toDomain(persistence: UserPersistence): User {
73
+ class UserToDomainMapper extends Mapper<UserPersistence, User> {
74
+ public build(persistence: UserPersistence): User {
79
75
  return User.create({
80
76
  id: Id.from(persistence.id),
81
77
  name: persistence.name,
@@ -84,8 +80,10 @@ class UserMapper extends BaseMapper<User, UserPersistence> {
84
80
  status: persistence.status as "active" | "inactive",
85
81
  });
86
82
  }
83
+ }
87
84
 
88
- toPersistence(domain: User): UserPersistence {
85
+ class UserToPersistenceMapper extends Mapper<User, UserPersistence> {
86
+ public build(domain: User): UserPersistence {
89
87
  return {
90
88
  id: domain.id.value,
91
89
  name: domain.name,
@@ -102,41 +100,51 @@ class UserMapper extends BaseMapper<User, UserPersistence> {
102
100
  // Mock Repository Implementation
103
101
  // ============================================================================
104
102
 
105
- class MockUserRepository extends BaseRepository<User, UserPersistence> {
103
+ class MockUserRepository extends Repository<User> {
106
104
  private store: Map<string, UserPersistence> = new Map();
107
105
 
108
- constructor() {
109
- super(new UserMapper());
106
+ constructor(
107
+ protected readonly mapperToDomain: Mapper<UserPersistence, User>,
108
+ protected readonly mapperToPersistence: Mapper<User, UserPersistence>
109
+ ) {
110
+ super();
111
+ }
112
+
113
+ get model() {
114
+ return "users";
115
+ }
116
+
117
+ async create(entity: User) {
118
+ const persistence = this.mapperToPersistence.build(entity);
119
+ this.store.set(entity.id.value, persistence);
120
+ }
121
+
122
+ async delete(entity: User): Promise<void> {
123
+ this.store.delete(entity.id.value);
110
124
  }
111
125
 
112
- protected async insertOne(data: UserPersistence): Promise<UserPersistence> {
113
- this.store.set(data.id, data);
114
- return data;
126
+ async count(criteria: Criteria<User>): Promise<number> {
127
+ return this.applyCriteria(criteria).then((result) => result.meta.total);
115
128
  }
116
129
 
117
- protected async updateOne(
118
- id: string,
119
- data: UserPersistence
120
- ): Promise<UserPersistence> {
121
- this.store.set(id, { ...data, updatedAt: new Date() });
122
- return data;
130
+ async exists(id: string): Promise<boolean> {
131
+ return this.store.has(id);
123
132
  }
124
133
 
125
- protected async deleteOne(id: string): Promise<void> {
126
- this.store.delete(id);
134
+ async find(criteria: Criteria<User>): Promise<PaginatedResult<User>> {
135
+ const result = await this.applyCriteria(criteria);
136
+ return result;
127
137
  }
128
138
 
129
- protected async findOneById(id: string): Promise<User | null> {
139
+ async findById(id: string): Promise<User | null> {
130
140
  const persistence = this.store.get(id);
131
141
  if (!persistence) return null;
132
- return this.mapper.toDomain(persistence);
142
+ return this.mapperToDomain.build(persistence);
133
143
  }
134
144
 
135
- protected async findMany(): Promise<User[]> {
136
- const persistence = Array.from(this.store.values());
137
- return this.mapper.toDomainList
138
- ? this.mapper.toDomainList(persistence)
139
- : persistence.map((p) => this.mapper.toDomain(p));
145
+ async update(entity: User): Promise<void> {
146
+ const persistence = this.mapperToPersistence.build(entity);
147
+ this.store.set(entity.id.value, persistence);
140
148
  }
141
149
 
142
150
  protected async applyCriteria(criteria: Criteria<User>) {
@@ -183,9 +191,7 @@ class MockUserRepository extends BaseRepository<User, UserPersistence> {
183
191
  pagination.offset + pagination.limit
184
192
  );
185
193
 
186
- const domains = this.mapper.toDomainList
187
- ? this.mapper.toDomainList(results)
188
- : results.map((p) => this.mapper.toDomain(p));
194
+ const domains = results.map((p) => this.mapperToDomain.build(p));
189
195
 
190
196
  return PaginatedResult.create(domains, pagination, total);
191
197
  }
@@ -221,7 +227,10 @@ describe("Repository", () => {
221
227
  let user3: User;
222
228
 
223
229
  beforeEach(() => {
224
- repository = new InMemoryRepository<User>();
230
+ repository = new InMemoryRepository<User>(
231
+ new UserToDomainMapper(),
232
+ new UserToPersistenceMapper()
233
+ );
225
234
 
226
235
  user1 = User.create({
227
236
  name: "Alice",
@@ -251,8 +260,8 @@ describe("Repository", () => {
251
260
 
252
261
  describe("save and findById", () => {
253
262
  it("should save and retrieve user", async () => {
254
- await repository.save(user1);
255
- const found = await repository.findById(user1.id);
263
+ await repository.create(user1);
264
+ const found = await repository.findById(user1.id.value);
256
265
 
257
266
  expect(found).toBeDefined();
258
267
  expect(found?.id.equals(user1.id)).toBe(true);
@@ -260,16 +269,16 @@ describe("Repository", () => {
260
269
  });
261
270
 
262
271
  it("should return null for non-existent id", async () => {
263
- const found = await repository.findById(new Id());
272
+ const found = await repository.findById(new Id().value);
264
273
  expect(found).toBeNull();
265
274
  });
266
275
 
267
276
  it("should update existing user", async () => {
268
- await repository.save(user1);
277
+ await repository.create(user1);
269
278
  user1.setName("Alice Updated");
270
- await repository.save(user1);
279
+ await repository.create(user1);
271
280
 
272
- const found = await repository.findById(user1.id);
281
+ const found = await repository.findById(user1.id.value);
273
282
  expect(found?.name).toBe("Alice Updated");
274
283
  });
275
284
  });
@@ -281,18 +290,18 @@ describe("Repository", () => {
281
290
  });
282
291
 
283
292
  it("should return all users", async () => {
284
- await repository.save(user1);
285
- await repository.save(user2);
286
- await repository.save(user3);
293
+ await repository.create(user1);
294
+ await repository.create(user2);
295
+ await repository.create(user3);
287
296
 
288
297
  const users = await repository.findAll();
289
298
  expect(users).toHaveLength(3);
290
299
  });
291
300
 
292
301
  it("should return users matching criteria", async () => {
293
- await repository.save(user1);
294
- await repository.save(user2);
295
- await repository.save(user3);
302
+ await repository.create(user1);
303
+ await repository.create(user2);
304
+ await repository.create(user3);
296
305
 
297
306
  const criteria = Criteria.create<User>().whereEquals(
298
307
  "status",
@@ -307,9 +316,9 @@ describe("Repository", () => {
307
316
 
308
317
  describe("find with Criteria", () => {
309
318
  beforeEach(async () => {
310
- await repository.save(user1);
311
- await repository.save(user2);
312
- await repository.save(user3);
319
+ await repository.create(user1);
320
+ await repository.create(user2);
321
+ await repository.create(user3);
313
322
  });
314
323
 
315
324
  it("should filter by equals", async () => {
@@ -364,8 +373,8 @@ describe("Repository", () => {
364
373
 
365
374
  describe("findOne", () => {
366
375
  it("should find first matching user", async () => {
367
- await repository.save(user1);
368
- await repository.save(user2);
376
+ await repository.create(user1);
377
+ await repository.create(user2);
369
378
 
370
379
  const criteria = Criteria.create<User>().whereEquals(
371
380
  "status",
@@ -390,40 +399,40 @@ describe("Repository", () => {
390
399
 
391
400
  describe("delete", () => {
392
401
  it("should delete by aggregate", async () => {
393
- await repository.save(user1);
402
+ await repository.create(user1);
394
403
  await repository.delete(user1);
395
404
 
396
- const found = await repository.findById(user1.id);
405
+ const found = await repository.findById(user1.id.value);
397
406
  expect(found).toBeNull();
398
407
  });
399
408
 
400
409
  it("should delete by id", async () => {
401
- await repository.save(user1);
402
- await repository.deleteById(user1.id);
410
+ await repository.create(user1);
411
+ await repository.delete(user1);
403
412
 
404
- const found = await repository.findById(user1.id);
413
+ const found = await repository.findById(user1.id.value);
405
414
  expect(found).toBeNull();
406
415
  });
407
416
  });
408
417
 
409
418
  describe("exists", () => {
410
419
  it("should return true when user exists", async () => {
411
- await repository.save(user1);
412
- const exists = await repository.exists(user1.id);
420
+ await repository.create(user1);
421
+ const exists = await repository.exists(user1.id.value);
413
422
  expect(exists).toBe(true);
414
423
  });
415
424
 
416
425
  it("should return false when user does not exist", async () => {
417
- const exists = await repository.exists(new Id());
426
+ const exists = await repository.exists(new Id().value);
418
427
  expect(exists).toBe(false);
419
428
  });
420
429
  });
421
430
 
422
431
  describe("count", () => {
423
432
  beforeEach(async () => {
424
- await repository.save(user1);
425
- await repository.save(user2);
426
- await repository.save(user3);
433
+ await repository.create(user1);
434
+ await repository.create(user2);
435
+ await repository.create(user3);
427
436
  });
428
437
 
429
438
  it("should count all users", async () => {
@@ -443,7 +452,7 @@ describe("Repository", () => {
443
452
 
444
453
  describe("saveMany", () => {
445
454
  it("should save multiple users", async () => {
446
- await repository.saveMany([user1, user2, user3]);
455
+ await repository.createMany([user1, user2, user3]);
447
456
 
448
457
  const users = await repository.findAll();
449
458
  expect(users).toHaveLength(3);
@@ -456,7 +465,10 @@ describe("Repository", () => {
456
465
  let user: User;
457
466
 
458
467
  beforeEach(() => {
459
- repository = new MockUserRepository();
468
+ repository = new MockUserRepository(
469
+ new UserToDomainMapper(),
470
+ new UserToPersistenceMapper()
471
+ );
460
472
  user = User.create({
461
473
  name: "John",
462
474
  email: "john@example.com",
@@ -470,16 +482,16 @@ describe("Repository", () => {
470
482
  });
471
483
 
472
484
  it("should save new user (insert)", async () => {
473
- expect(user.isNew).toBe(true);
474
- await repository.save(user);
485
+ expect(user.isNew()).toBe(true);
486
+ await repository.create(user);
475
487
 
476
- const found = await repository.findById(user.id);
488
+ const found = await repository.findById(user.id.value);
477
489
  expect(found).toBeDefined();
478
490
  expect(found?.name).toBe("John");
479
491
  });
480
492
 
481
493
  it("should update existing user", async () => {
482
- await repository.save(user);
494
+ await repository.create(user);
483
495
 
484
496
  // Simulate existing user
485
497
  const existingUser = User.create({
@@ -490,16 +502,16 @@ describe("Repository", () => {
490
502
  status: "inactive",
491
503
  });
492
504
 
493
- await repository.save(existingUser);
505
+ await repository.create(existingUser);
494
506
 
495
- const found = await repository.findById(user.id);
507
+ const found = await repository.findById(user.id.value);
496
508
  expect(found?.name).toBe("John Updated");
497
509
  expect(found?.age).toBe(29);
498
510
  });
499
511
 
500
512
  it("should convert between domain and persistence", async () => {
501
- await repository.save(user);
502
- const found = await repository.findById(user.id);
513
+ await repository.create(user);
514
+ const found = await repository.findById(user.id.value);
503
515
 
504
516
  // Should be a domain object
505
517
  expect(found).toBeInstanceOf(User);
@@ -520,7 +532,7 @@ describe("Repository", () => {
520
532
  status: "inactive",
521
533
  });
522
534
 
523
- await Promise.all([repository.save(user1), repository.save(user2)]);
535
+ await Promise.all([repository.create(user1), repository.create(user2)]);
524
536
 
525
537
  const result = await repository.find(
526
538
  Criteria.create<User>()
@@ -535,10 +547,12 @@ describe("Repository", () => {
535
547
  });
536
548
 
537
549
  describe("Mapper", () => {
538
- let mapper: UserMapper;
550
+ let toDomainMapper: UserToDomainMapper;
551
+ let toPersistenceMapper: UserToPersistenceMapper;
539
552
 
540
553
  beforeEach(() => {
541
- mapper = new UserMapper();
554
+ toDomainMapper = new UserToDomainMapper();
555
+ toPersistenceMapper = new UserToPersistenceMapper();
542
556
  });
543
557
 
544
558
  it("should convert from persistence to domain", () => {
@@ -552,7 +566,7 @@ describe("Repository", () => {
552
566
  updatedAt: new Date(),
553
567
  };
554
568
 
555
- const domain = mapper.toDomain(persistence);
569
+ const domain = toDomainMapper.build(persistence);
556
570
 
557
571
  expect(domain).toBeInstanceOf(User);
558
572
  expect(domain.id.value).toBe("123");
@@ -571,7 +585,7 @@ describe("Repository", () => {
571
585
  status: "active",
572
586
  });
573
587
 
574
- const persistence = mapper.toPersistence(domain);
588
+ const persistence = toPersistenceMapper.build(domain);
575
589
 
576
590
  expect(persistence.id).toBe("123");
577
591
  expect(persistence.name).toBe("John");
@@ -604,7 +618,7 @@ describe("Repository", () => {
604
618
  },
605
619
  ];
606
620
 
607
- const domainList = mapper.toDomainList(persistenceList);
621
+ const domainList = persistenceList.map((p) => toDomainMapper.build(p));
608
622
 
609
623
  expect(domainList).toHaveLength(2);
610
624
  expect(domainList[0]).toBeInstanceOf(User);
@@ -630,7 +644,9 @@ describe("Repository", () => {
630
644
  }),
631
645
  ];
632
646
 
633
- const persistenceList = mapper.toPersistenceList(domainList);
647
+ const persistenceList = domainList.map((d) =>
648
+ toPersistenceMapper.build(d)
649
+ );
634
650
 
635
651
  expect(persistenceList).toHaveLength(2);
636
652
  expect(persistenceList[0].id).toBe("1");
@@ -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
+ });
@@ -1,52 +1,52 @@
1
- import { Address } from "./utils";
2
-
3
- describe("Value Object", () => {
4
- it("should create immutable value object", () => {
5
- const address = new Address({
6
- street: "Main St",
7
- city: "NYC",
8
- zipCode: "10001",
9
- });
10
-
11
- expect(address.street).toBe("Main St");
12
- expect(address.city).toBe("NYC");
13
- });
14
-
15
- it("should compare value objects by value", () => {
16
- const address1 = new Address({
17
- street: "Main St",
18
- city: "NYC",
19
- zipCode: "10001",
20
- });
21
-
22
- const address2 = new Address({
23
- street: "Main St",
24
- city: "NYC",
25
- zipCode: "10001",
26
- });
27
-
28
- const address3 = new Address({
29
- street: "Broadway",
30
- city: "NYC",
31
- zipCode: "10001",
32
- });
33
-
34
- expect(address1.equals(address2)).toBe(true);
35
- expect(address1.equals(address3)).toBe(false);
36
- });
37
-
38
- it("should convert value object to JSON", () => {
39
- const address = new Address({
40
- street: "Main St",
41
- city: "NYC",
42
- zipCode: "10001",
43
- });
44
-
45
- const json = address.toJson();
46
- expect(json).toEqual({
47
- street: "Main St",
48
- city: "NYC",
49
- zipCode: "10001",
50
- });
51
- });
52
- });
1
+ import { Address } from "./utils";
2
+
3
+ describe("Value Object", () => {
4
+ it("should create immutable value object", () => {
5
+ const address = new Address({
6
+ street: "Main St",
7
+ city: "NYC",
8
+ zipCode: "10001",
9
+ });
10
+
11
+ expect(address.street).toBe("Main St");
12
+ expect(address.city).toBe("NYC");
13
+ });
14
+
15
+ it("should compare value objects by value", () => {
16
+ const address1 = new Address({
17
+ street: "Main St",
18
+ city: "NYC",
19
+ zipCode: "10001",
20
+ });
21
+
22
+ const address2 = new Address({
23
+ street: "Main St",
24
+ city: "NYC",
25
+ zipCode: "10001",
26
+ });
27
+
28
+ const address3 = new Address({
29
+ street: "Broadway",
30
+ city: "NYC",
31
+ zipCode: "10001",
32
+ });
33
+
34
+ expect(address1.equals(address2)).toBe(true);
35
+ expect(address1.equals(address3)).toBe(false);
36
+ });
37
+
38
+ it("should convert value object to JSON", () => {
39
+ const address = new Address({
40
+ street: "Main St",
41
+ city: "NYC",
42
+ zipCode: "10001",
43
+ });
44
+
45
+ const json = address.toJson();
46
+ expect(json).toEqual({
47
+ street: "Main St",
48
+ city: "NYC",
49
+ zipCode: "10001",
50
+ });
51
+ });
52
+ });