人を表すドメインエンティティがあるとします。 (TypeScriptの例)
class Person {
constructor(public name: string) {}
}
ここで、ドメインの他の部分は集約の外部からPersonエンティティを参照する必要があるため、データベースでPersonのIDを公開する必要があります。
class Person {
constructor(public id: number, public name: string) {}
}
ここで、id
はnull可能ではないため、ドメインレイヤーで自由に新しいインスタンスを作成することはできません。
const p = new Person(undefined, 'Patrick');
error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'number'.
さて、私はid
プロパティをおそらくundefined
-またはnull
にする必要がありますか?この問題が他の型システムにどのように変換されるかはわかりません。
class Person {
constructor(public id: number | undefined, public name: string) {}
}
const p = new Person(undefined, 'Patrick');
しかし今、私はそれが起こらないと確信している場所でIDをnull可能にするでしょう:
async function doSomething(): Promise<number> {
const p = await personRepository.getPersonById(id);
// let's say for some reason I'll use the ID
return p.id + 500;
error TS2532: Object is possibly 'undefined'.
}
常に存在するとは思うがそうではない可能性があるすべてのプロパティについて、この例ではIDですが、作成日やその他のデータベース管理対象も含まれている可能性があるため、その実行パスを保護する必要があります。
async function doSomething(): Promise<number> {
const p = await personRepository.getPersonById(id);
// let's say for some reason I'll use the ID
if (p.id === undefined) throw 'Should not happen';
return p.id + 500;
}
これにより、コードベースに多くの残骸が追加されます。
では、ここでの最善の行動方針は何でしょうか?ドメインのIDを公開しないように試みますか(難しいようです)? idをnull可能にして、リポジトリが異なる扱いをする偽物を提供しないでください(臭いようです)? id
タイプをnumber
から別のタイプに強化しますか?
この問題は、実際のビジネスデータをデータベース関連のものから分離することで解決しました。人の例を見てみましょう。
export interface Person {
name: string;
age: number;
favoriteColor: string;
}
これで、そのインターフェース(またはクラス)を、データベースに関連するすべてのものを保持するより一般的なインターフェースでラップできます。
export interface ModifiableEntity<T> {
id: string;
createdAt: Date;
createdBy: string;
modifiedAt?: Date;
modifiedBy?: string;
entity: T;
}
このように、データベースのメタデータからデータ(年齢、名前など)をうまく分離できます。
バックエンドから読み取ると、次のようなメッセージが表示されます。
[
{
id: '1',
createdAt: xxx,
createdBy: 'Me',
entity: {
name: 'Test Person 1',
age: 47,
favoriteColor: 'Blue',
}
},
{
id: '2',
createdAt: xxx,
createdBy: 'You',
entity: {
name: 'Test Person 2',
age: 11,
favoriteColor: 'Red',
}
}
]
バックエンドで人物を作成する場合、PUT
エンドポイント/person
を呼び出すことができます。個人を更新する場合は、POST
エンドポイント/person/{id}
を呼び出します。 IDは、ペイロードではなくURLの一部です。
また、ドラフトの人物と一緒に作業しているときに、Optional<Person>
などのヘルパーインターフェイスを使用して、作成中に不完全な人物オブジェクトを許可することもできます。
IDを持つ一般的なDbEntityタイプを作成し、ドメインタイプをデータベース固有のものから解放することができます。そのようです:
type DbEntity<T> = T & { id: number };
class Person {
constructor(public name: string) {}
}
interface PersonRepository {
getPersonById(id: number): DbEntity<Person>;
saveNewPerson(person: Person): DbEntity<Person>;
updatePerson(person: DbEntity<Person>): void;
}