@woltz/rich-domain 0.2.1 → 0.2.2

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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/mapper.d.ts +4 -0
  7. package/dist/mapper.d.ts.map +1 -0
  8. package/dist/mapper.js +3 -0
  9. package/dist/mapper.js.map +1 -0
  10. package/dist/paginated-result.d.ts.map +1 -1
  11. package/dist/paginated-result.js +0 -3
  12. package/dist/paginated-result.js.map +1 -1
  13. package/dist/repository/base-repository.d.ts +25 -48
  14. package/dist/repository/base-repository.d.ts.map +1 -1
  15. package/dist/repository/base-repository.js +7 -51
  16. package/dist/repository/base-repository.js.map +1 -1
  17. package/dist/repository/in-memory-repository.d.ts +12 -8
  18. package/dist/repository/in-memory-repository.d.ts.map +1 -1
  19. package/dist/repository/in-memory-repository.js +18 -10
  20. package/dist/repository/in-memory-repository.js.map +1 -1
  21. package/dist/repository/index.d.ts +2 -3
  22. package/dist/repository/index.d.ts.map +1 -1
  23. package/dist/repository/index.js +2 -2
  24. package/dist/repository/index.js.map +1 -1
  25. package/dist/types/index.d.ts +0 -1
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/index.js +0 -1
  28. package/dist/types/index.js.map +1 -1
  29. package/dist/types/unit-of-work.d.ts +2 -2
  30. package/dist/types/unit-of-work.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/index.ts +1 -0
  33. package/src/mapper.ts +3 -0
  34. package/src/paginated-result.ts +1 -8
  35. package/src/repository/base-repository.ts +27 -115
  36. package/src/repository/in-memory-repository.ts +28 -16
  37. package/src/repository/index.ts +2 -3
  38. package/src/types/index.ts +0 -1
  39. package/src/types/unit-of-work.ts +3 -3
  40. package/tests/repository.test.ts +92 -78
  41. package/src/repository/mapper.ts +0 -74
  42. package/src/types/repository.ts +0 -51
@@ -2,12 +2,10 @@
2
2
  // Base Repository - Abstract implementation with common logic
3
3
  // ============================================================================
4
4
 
5
- import type { Id } from "../id";
6
5
  import type { Aggregate } from "../entity";
7
6
  import type { Criteria } from "../criteria";
8
7
  import { PaginatedResult } from "../paginated-result";
9
- import type { IRepository } from "../types";
10
- import type { IMapper } from "./mapper";
8
+ import { Mapper } from "../mapper";
11
9
 
12
10
  /**
13
11
  * Abstract base repository
@@ -33,120 +31,34 @@ import type { IMapper } from "./mapper";
33
31
  * }
34
32
  * ```
35
33
  */
36
- export abstract class BaseRepository<
37
- TDomain extends Aggregate<any>,
38
- TPersistence = any
39
- > implements IRepository<TDomain>
40
- {
41
- constructor(protected readonly mapper: IMapper<TDomain, TPersistence>) {}
42
34
 
43
- // ============================================================================
44
- // Abstract methods - Must be implemented by subclasses
45
- // ============================================================================
46
-
47
- /**
48
- * Insert new record in database
49
- */
50
- protected abstract insertOne(data: TPersistence): Promise<TPersistence>;
51
-
52
- /**
53
- * Update existing record in database
54
- */
55
- protected abstract updateOne(
56
- id: string,
57
- data: TPersistence
58
- ): Promise<TPersistence>;
59
-
60
- /**
61
- * Delete record from database
62
- */
63
- protected abstract deleteOne(id: string): Promise<void>;
64
-
65
- /**
66
- * Find record by ID in database
67
- */
68
- protected abstract findOneById(id: string): Promise<TDomain | null>;
69
-
70
- /**
71
- * Find all records in database (no filtering)
72
- */
73
- protected abstract findMany(): Promise<TDomain[]>;
74
-
75
- /**
76
- * Apply criteria to query (filtering, ordering, pagination)
77
- * Returns [data, total]
78
- */
79
- protected abstract applyCriteria(
80
- criteria: Criteria<TDomain>
81
- ): Promise<PaginatedResult<TDomain>>;
82
-
83
- /**
84
- * Count records matching criteria
85
- */
86
- protected abstract countByCriteria(
87
- criteria?: Criteria<TDomain>
88
- ): Promise<number>;
89
-
90
- /**
91
- * Check if record exists by ID
92
- */
93
- protected abstract existsById(id: string): Promise<boolean>;
94
-
95
- // ============================================================================
96
- // Public API - Implemented using abstract methods
97
- // ============================================================================
98
-
99
- async findById(id: Id): Promise<TDomain | null> {
100
- const domain = await this.findOneById(id.value);
101
- if (!domain) return null;
102
- return domain;
103
- }
104
-
105
- async find(criteria: Criteria<TDomain>): Promise<PaginatedResult<TDomain>> {
106
- return await this.applyCriteria(criteria);
107
- }
108
-
109
- async findAll(criteria?: Criteria<TDomain>): Promise<TDomain[]> {
110
- if (criteria) {
111
- const result = await this.find(criteria);
112
- return result.data;
113
- }
114
-
115
- const domains = await this.findMany();
116
- return domains;
117
- }
118
-
119
- async findOne(criteria: Criteria<TDomain>): Promise<TDomain | null> {
120
- // Limit to 1 result
121
- const limitedCriteria = criteria.clone().limit(1);
122
- const result = await this.find(limitedCriteria);
123
-
124
- return result.data.length > 0 ? result.data[0] : null;
125
- }
126
-
127
- async save(aggregate: TDomain): Promise<void> {
128
- const persistence = this.mapper.toPersistence(aggregate);
129
-
130
- if (aggregate.isNew) {
131
- await this.insertOne(persistence);
132
- } else {
133
- await this.updateOne(aggregate.id.value, persistence);
134
- }
135
- }
136
-
137
- async delete(aggregate: TDomain): Promise<void> {
138
- await this.deleteOne(aggregate.id.value);
139
- }
35
+ export abstract class ReadRepository<Agg extends Aggregate<any>> {
36
+ abstract find(criteria: Criteria<Agg>): Promise<PaginatedResult<Agg>>;
37
+ abstract findById(id: string): Promise<Agg | null>;
38
+ abstract count(criteria: Criteria<Agg>): Promise<number>;
39
+ abstract exists(id: string): Promise<boolean>;
40
+ }
140
41
 
141
- async deleteById(id: Id): Promise<void> {
142
- await this.deleteOne(id.value);
143
- }
42
+ export abstract class WriteRepository<Agg extends Aggregate<any>> {
43
+ abstract create(entity: Agg): Promise<void>;
44
+ abstract update(entity: Agg): Promise<void>;
45
+ abstract delete(entity: Agg): Promise<void>;
46
+ }
144
47
 
145
- async exists(id: Id): Promise<boolean> {
146
- return this.existsById(id.value);
147
- }
48
+ export abstract class WriteAndRead<Agg extends Aggregate<any>> {
49
+ abstract find(criteria: Criteria<Agg>): Promise<PaginatedResult<Agg>>;
50
+ abstract findById(id: string): Promise<Agg | null>;
51
+ abstract create(entity: Agg): Promise<void>;
52
+ abstract update(entity: Agg): Promise<void>;
53
+ abstract delete(entity: Agg): Promise<void>;
54
+ abstract count(criteria: Criteria<Agg>): Promise<number>;
55
+ abstract exists(id: string): Promise<boolean>;
56
+ }
148
57
 
149
- async count(criteria?: Criteria<TDomain>): Promise<number> {
150
- return this.countByCriteria(criteria);
151
- }
58
+ export abstract class Repository<
59
+ TDomain extends Aggregate<any>
60
+ > extends WriteAndRead<TDomain> {
61
+ protected abstract readonly mapperToDomain: Mapper<unknown, TDomain>;
62
+ protected abstract readonly mapperToPersistence: Mapper<TDomain, unknown>;
63
+ abstract get model(): any;
152
64
  }
@@ -2,11 +2,11 @@
2
2
  // In-Memory Repository - Perfect for testing
3
3
  // ============================================================================
4
4
 
5
- import type { Id } from "../id";
6
5
  import type { Aggregate } from "../entity";
7
6
  import type { Criteria } from "../criteria";
8
7
  import { PaginatedResult } from "../paginated-result";
9
- import type { IRepository } from "../types";
8
+ import { Repository } from "./base-repository";
9
+ import { Mapper } from "../mapper";
10
10
 
11
11
  /**
12
12
  * In-memory repository implementation
@@ -23,13 +23,25 @@ import type { IRepository } from "../types";
23
23
  * );
24
24
  * ```
25
25
  */
26
- export class InMemoryRepository<TDomain extends Aggregate<any>>
27
- implements IRepository<TDomain>
28
- {
26
+ export class InMemoryRepository<
27
+ TDomain extends Aggregate<any>
28
+ > extends Repository<TDomain> {
29
29
  protected items: Map<string, TDomain> = new Map();
30
30
 
31
- async findById(id: Id): Promise<TDomain | null> {
32
- return this.items.get(id.value) || null;
31
+ constructor(
32
+ protected readonly mapperToDomain: Mapper<unknown, TDomain>,
33
+ protected readonly mapperToPersistence: Mapper<TDomain, unknown>
34
+ ) {
35
+ super();
36
+ }
37
+
38
+ get model(): any {
39
+ // your database table name
40
+ return "inMemory";
41
+ }
42
+
43
+ async findById(id: string): Promise<TDomain | null> {
44
+ return this.items.get(id) || null;
33
45
  }
34
46
 
35
47
  async find(criteria: Criteria<TDomain>): Promise<PaginatedResult<TDomain>> {
@@ -51,13 +63,17 @@ export class InMemoryRepository<TDomain extends Aggregate<any>>
51
63
  return result.data.length > 0 ? result.data[0] : null;
52
64
  }
53
65
 
54
- async save(aggregate: TDomain): Promise<void> {
66
+ async create(aggregate: TDomain): Promise<void> {
55
67
  this.items.set(aggregate.id.value, aggregate);
56
68
  }
57
69
 
58
- async saveMany(aggregates: TDomain[]): Promise<void> {
70
+ async update(entity: TDomain): Promise<void> {
71
+ this.items.set(entity.id.value, entity);
72
+ }
73
+
74
+ async createMany(aggregates: TDomain[]): Promise<void> {
59
75
  for (const aggregate of aggregates) {
60
- await this.save(aggregate);
76
+ await this.create(aggregate);
61
77
  }
62
78
  }
63
79
 
@@ -65,12 +81,8 @@ export class InMemoryRepository<TDomain extends Aggregate<any>>
65
81
  this.items.delete(aggregate.id.value);
66
82
  }
67
83
 
68
- async deleteById(id: Id): Promise<void> {
69
- this.items.delete(id.value);
70
- }
71
-
72
- async exists(id: Id): Promise<boolean> {
73
- return this.items.has(id.value);
84
+ async exists(id: string): Promise<boolean> {
85
+ return this.items.has(id);
74
86
  }
75
87
 
76
88
  async count(criteria?: Criteria<TDomain>): Promise<number> {
@@ -3,11 +3,10 @@
3
3
  // ============================================================================
4
4
 
5
5
  // Mapper
6
- export { BaseMapper } from "./mapper";
7
- export type { IMapper } from "./mapper";
6
+ export { Mapper } from "../mapper";
8
7
 
9
8
  // Base implementations
10
- export { BaseRepository } from "./base-repository";
9
+ export { Repository as BaseRepository } from "./base-repository";
11
10
  export { InMemoryRepository } from "./in-memory-repository";
12
11
 
13
12
  // Unit of Work
@@ -3,5 +3,4 @@ export * from "./domain";
3
3
  export * from "./history-tracker";
4
4
  export * from "./standard-schema";
5
5
  export * from "./utils";
6
- export * from "./repository";
7
6
  export * from "./unit-of-work";
@@ -1,5 +1,5 @@
1
1
  import { Aggregate } from "../entity";
2
- import { IRepository } from "./repository";
2
+ import { Repository } from "../repository/base-repository";
3
3
 
4
4
  /**
5
5
  * Transaction context for Unit of Work
@@ -41,6 +41,6 @@ export interface IUnitOfWork {
41
41
  * Get repository within transaction context
42
42
  */
43
43
  getRepository<TDomain extends Aggregate<any>>(
44
- repository: new (...args: any[]) => IRepository<TDomain>
45
- ): IRepository<TDomain>;
44
+ repository: new (...args: any[]) => Repository<TDomain>
45
+ ): Repository<TDomain>;
46
46
  }
@@ -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";
110
115
  }
111
116
 
112
- protected async insertOne(data: UserPersistence): Promise<UserPersistence> {
113
- this.store.set(data.id, data);
114
- return data;
117
+ async create(entity: User) {
118
+ const persistence = this.mapperToPersistence.build(entity);
119
+ this.store.set(entity.id.value, persistence);
115
120
  }
116
121
 
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;
122
+ async delete(entity: User): Promise<void> {
123
+ this.store.delete(entity.id.value);
123
124
  }
124
125
 
125
- protected async deleteOne(id: string): Promise<void> {
126
- this.store.delete(id);
126
+ async count(criteria: Criteria<User>): Promise<number> {
127
+ return this.applyCriteria(criteria).then((result) => result.meta.total);
128
+ }
129
+
130
+ async exists(id: string): Promise<boolean> {
131
+ return this.store.has(id);
127
132
  }
128
133
 
129
- protected async findOneById(id: string): Promise<User | null> {
134
+ async find(criteria: Criteria<User>): Promise<PaginatedResult<User>> {
135
+ const result = await this.applyCriteria(criteria);
136
+ return result;
137
+ }
138
+
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",
@@ -471,15 +483,15 @@ describe("Repository", () => {
471
483
 
472
484
  it("should save new user (insert)", async () => {
473
485
  expect(user.isNew).toBe(true);
474
- await repository.save(user);
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,7 @@ describe("Repository", () => {
630
644
  }),
631
645
  ];
632
646
 
633
- const persistenceList = mapper.toPersistenceList(domainList);
647
+ const persistenceList = domainList.map((d) => toPersistenceMapper.build(d));
634
648
 
635
649
  expect(persistenceList).toHaveLength(2);
636
650
  expect(persistenceList[0].id).toBe("1");