テスト前提で設計したWebアプリのハンズオン - 読書管理アプリ その2

前回のおさらい その1では ValueObject・Policy・Entity を作った。ポイントは「フレームワークを一切使わないので、テストがただの関数呼び出しになる」という体験だった。 今回はいよいよ Service 層に入る。ここが設計の核心だ。 複数のクラスをまたぐフローを、DI(依存性の注入)を使って DB から切り離す。 今回追加したもの src/ domain/ entity/ User.ts # 追加 Review.ts # 追加 valueobject/ UserName.ts # 追加 ReviewComment.ts # 追加 repository/ BookRepository.ts # 追加(Interface) service/ BookShelfService.ts # 追加 User と Review を追加する まず Entity の準備。今回は単純なので ValueObject から作る。 UserName export class UserName { constructor(public readonly value: string) { if (!value || value.trim().length === 0) { throw new Error("UserNameは空にできない"); } if (value.length > 50) { throw new Error("UserNameは50文字以内"); } } } trim() してから空チェックしているのがポイント。スペースだけのユーザー名を弾く。 ReviewComment export class ReviewComment { constructor(public readonly value: string) { if (!value || value.trim().length === 0) { throw new Error("ReviewCommentは空にできない"); } if (value.length > 1000) { throw new Error("ReviewCommentは1000文字以内"); } } } User Entity import { UserName } from "../valueobject/UserName"; export class User { constructor( public readonly id: string, public readonly name: UserName, ) {} } User 自体はシンプルだ。ロジックがない。ビジネスルールが増えれば changeXxx() メソッドが生える設計だが、今は id と name を持つだけでいい。 ...

March 4, 2026 · 4 min