-
Notifications
You must be signed in to change notification settings - Fork 1
/
entity.ts
72 lines (56 loc) · 1.62 KB
/
entity.ts
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
import { v4 as uuid } from 'uuid';
export interface IEntity {
readonly id: string;
// multi-tenant application support
// Note: `null` if not required/single-tenant
readonly tenantId: string | null;
equals(object?: IEntity): boolean;
}
export type IEntityCoreData = {
[key: string]: unknown;
id?: IEntity['id'];
tenantId?: IEntity['tenantId'];
};
const isEntity = <T extends IEntityCoreData>(v: unknown): v is Entity<T> =>
v instanceof Entity;
/**
* An object whose definition is based on `identity` over just its attributes.
*
* Also known as `Reference Objects`.
*/
export abstract class Entity<T extends IEntityCoreData = IEntityCoreData>
implements IEntity
{
private readonly _id: IEntity['id'];
private readonly _tenantId: IEntity['tenantId'];
protected readonly _data: Omit<T, 'id' | 'tenantId'>;
constructor(data: T) {
// Optional `id`: allow for re-consituting objects from persistence
this._id = data?.id ?? uuid();
this._tenantId = data?.tenantId ?? null;
delete data?.id;
delete data?.tenantId;
this._data = data ?? {};
}
public get id() {
return this._id;
}
public get tenantId() {
return this._tenantId;
}
public equals(object?: Entity<T>): boolean {
if (object === null || object === undefined || !isEntity(object)) {
return false;
}
return Object.is(this, object) || this._id === object.id;
}
}
//
// Utility Types
//
/**
* Create the public interface of the resulting Entity.
*
* This allows you to depend on an abstraction over a concretion.
*/
export type BuildEntityInterface<T> = IEntity & Exclude<T, 'IEntityCoreData'>;