-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathentity.ts
More file actions
114 lines (105 loc) · 2.75 KB
/
Copy pathentity.ts
File metadata and controls
114 lines (105 loc) · 2.75 KB
1
2
3
4
5
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
/**
* Entity Constructor Interface
*
* Defines the constructor signature for Entity classes.
* Entities are objects with unique identity that are defined by their ID rather than their attributes.
*/
interface EntityConstructor {
new <ID extends any, Struct extends Record<string, any>>(
id: ID,
struct: Struct,
): Omit<_Entity<ID, Struct>, `_${string}`> & Readonly<Struct>;
}
/**
* Base Entity Class
*
* Abstract base class for all domain entities.
* Entities have:
* - Unique identity (ID)
* - Mutable state (struct)
* - Equality based on ID
*
* @template ID - Type of the entity's unique identifier
* @template Struct - Type of the entity's properties/state
*
* @example
* ```typescript
* export type CandidateStruct = {
* name: string;
* email: Email;
* phone?: Phone;
* };
*
* export class Candidate extends Entity<UUID, CandidateStruct> {
* updateEmail(newEmail: Email): void {
* this.props.email = newEmail;
* }
*
* get email(): Email {
* return this.props.email;
* }
* }
*
* const candidateId = UUID.generate();
* const candidate = new Candidate(candidateId, {
* name: 'John Doe',
* email: Email.create('john@example.com'),
* phone: Phone.create('+1234567890')
* });
*
* console.log(candidate.id); // UUID
* console.log(candidate.name); // 'John Doe'
* console.log(candidate.email); // Email instance
* ```
*/
abstract class _Entity<ID extends any, Struct extends Record<string, any>> {
readonly #id: ID;
#struct: Struct;
protected constructor(
private _id: ID,
private _struct: Struct,
) {
this.#id = this._id;
this.#struct = this._struct;
const entriesStruct = Object.keys(this.#struct) as Array<keyof Struct>;
// Define getters for all struct properties
for (let i = 0; i < entriesStruct.length; i++) {
const key = entriesStruct[i];
if (!key) continue;
Object.defineProperty(this, key, {
get() {
return this.#struct[key];
},
configurable: true,
enumerable: true,
});
}
}
/**
* Compares two entities for equality based on their IDs
*
* @param other - Another entity to compare with
* @returns true if entities have the same ID, false otherwise
*/
equals(other: _Entity<ID, Struct>): boolean {
return this.#id === other.id;
}
/**
* Gets the entity's unique identifier
*
* @returns The entity's ID
*/
get id(): ID {
return this.#id;
}
}
/**
* Entity class export with proper typing
*
* Use this as the base class for your domain entities.
*/
export const Entity = _Entity as unknown as EntityConstructor;
/**
* Entity type export for type annotations
*/
export type Entity<ID extends never, Struct extends Record<string, never>> = _Entity<ID, Struct>;