初めに
こんにちは。
PS/SLの佐々木です。
最近輪読会でDDDについての書籍を扱っているのですが、その中でValueObjectを作るのか作らないのか論争が巻き起こっています。
私自身作るに越したことはないと思うのですが、実装量が多くなるのと、必要なものだけ作ればいいのではないかと思う反面、作る作らないの判断が人によると一貫性のないコードになってしまう懸念点があります。
そこで今回はTypescriptでDomainObjectaのコンストラクタで定義されているプロパティからValueObjectを自動生成する ts-vo-generator
というライブラリを作成してみました。
ts-vo-generatorの使い方
こちらのライブラリはnpmとyarnで公開しています。(npm registry)
今回はnpmでインストールする例を紹介します。
npm install -g ts-vo-generator
使用する際には
npx typescript-value-object-generator <input_file> <output_directory>
このように使用します。
input_file
には解析対象のclassのパスを指定し、output_directory
にはValueObjectを生成するディレクトリを指定します。
実際に使用してみる
今回は以下のようなクラスからValuObjectを自動生成してみます。
type MountainType = {
id: number;
name: string;
elevation: number;
description?: string;
range: string;
}
// Mountain.ts
export class Mountain {
private constructor(
private id: number,
private name: string,
private elevation: number,
private range: string,
private description?: string
) {}
static new(props: MountainType): Mountain {
return new Mountain(
props.id,
props.name,
props.elevation,
props.range,
props.description
);
}
public genMessage(): string {
return `${this.name} の標高は ${this.elevation} mです。`;
}
}
以下のコマンドを実行します。
npx ts-vo-generator ./src/model/Mountain.ts ./src/ValueObject
実行すると
/src/ValueObject
配下にDescription.ts
やId.ts
が生成されていることが確認できます。
// Id.ts
export class Id {
private constructor(private readonly value: number) {}
static create(value: number): Id {
if (!Id.isValid(value)) {
throw new Error('Invalid value');
}
return new Id(value);
}
getValue(): number {
return this.value;
}
private static isValid(value: number): boolean {
// validation logic here
return true;
}
}
生成されたコードを見てみると上記のようなValueObjectのスケルトンコードが生成されています。
この後は好きなロジックを追加していきます。
また一度生成したものに関して再度生成しようとすると上書きするか処理をスキップするかを聞かれます。
今後の展望
現在のts-vo-generator
ではValueObjectを作成後、元のClassの方にも手動でValueObjectを定義しないといけません。
近いうちにこちらの対応も行っていきたいと思います。
最終的には以下のようなところまで自動で生成したいと思っています。(現時点ではこの修正は手動です)
import { Id } from '../ValueObject/Id';
import { Description } from '../ValueObject/Description';
import { Elevation } from '../ValueObject/Elevation';
import { Name } from '../ValueObject/Name';
import { Range } from '../ValueObject/Range';
type MountainType = {
id: Id;
name: Name;
elevation: Elevation;
description?: Description;
range: Range;
}
// Mountain.ts
export class Mountain {
private constructor(
private id: Id,
private name: Name,
private elevation: Elevation,
private range: Range,
private description?: Description
) {}
static new(props: MountainType): Mountain {
return new Mountain(
props.id,
props.name,
props.elevation,
props.range,
props.description
);
}
public genMessage(): string {
return `${this.name.getValue()} の標高は ${this.elevation.getValue()} mです。`;
}
}
終わりに
最後まで読んでいただきありがとうございました。
もしよろしけべばStarやコントリビュートお待ちしています。