概要
「オブジェクトとは、データではなく意味を持った構造体である」
データベースのレコードをそのままJSオブジェクトに変換して“業務ロジックを書く”のでは、ビジネスの意味をコードに閉じ込めることはできない。
本稿では、エンティティ・値オブジェクト・不変性・責務分離・ユースケースとの接続戦略を軸に、ビジネスロジックとコード構造の一致を目指す設計思想を解説する。
1. ドメインモデルとエンティティの定義
- エンティティ(Entity):一意のIDとライフサイクルを持つ「業務的な存在」
- 値オブジェクト(Value Object):IDを持たず、属性によって等価性が決まる
// エンティティ例
class User {
constructor({ id, name, email }) {
this.id = id;
this.name = name;
this.email = email;
}
changeEmail(newEmail) {
if (!newEmail.includes('@')) throw new Error('Invalid email');
this.email = newEmail;
}
}
→ ✅ IDを持ち、業務的に“振る舞う”データ
2. 値オブジェクトの設計
class Email {
constructor(value) {
if (!value.includes('@')) throw new Error('Invalid Email');
this.value = value;
}
equals(other) {
return this.value === other.value;
}
}
- ✅ 不変性を前提とする
- ✅ ロジック内でのデータ検証と表現の責務を統一
→ 値に意味を持たせ、原始型では表せない“意図”をコード化
3. 不変性の維持と副作用の排除
// 値オブジェクトは再代入ではなく再生成
const newEmail = new Email('new@example.com');
user.changeEmail(newEmail.value);
→ ✅ 値を変えるのではなく、“新しい値で置き換える”
4. エンティティとユースケースの分離
❌ アプリケーション層でエンティティを破壊的に扱う
user.email = 'hoge@foo.com'; // ❌ 意味を持たない変更
✅ ドメイン層の振る舞いをユースケースが使う構図に
// usecases/changeUserEmail.js
export function changeUserEmail(userRepo, userId, newEmailStr) {
const user = userRepo.findById(userId);
const newEmail = new Email(newEmailStr);
user.changeEmail(newEmail.value);
userRepo.save(user);
}
- ✅ ユースケースが振る舞いを**“呼び出す”**
- ✅ 振る舞いはエンティティに“閉じ込める”
5. 永続化とドメインモデルの変換(データマッパー)
// infra/userMapper.js
export function toDomain(record) {
return new User({
id: record.id,
name: record.name,
email: record.email
});
}
→ ✅ DBモデルとドメインモデルを“分離”
→ ✅ スキーマ変更に強く、責務が明確
6. 構造と設計例
/domain
/user
User.js // エンティティ
Email.js // 値オブジェクト
/usecases
changeUserEmail.js
/infra
userRepository.js
userMapper.js
- ✅ 責務に応じて階層分離
- ✅
ドメイン層
にロジック、ユースケース層
にアプリケーション操作、インフラ層
にデータアクセス
設計判断フロー
① IDを持ち、状態変化がある? → エンティティで表現
② 単なる属性で等価性が決まる? → 値オブジェクトに切り出す
③ ドメイン知識が埋まっていないか? → エンティティ/VOに閉じ込める
④ DB構造と混ざっていないか? → 永続層とはマッパーで分離
⑤ ユースケースがドメインを“操作”していないか? → 振る舞いを呼び出す構造へ
よくあるミスと対策
❌ ドメインロジックがアプリケーション層に散乱
→ ✅ モデルに**“意味のある振る舞い”**を定義し閉じ込める
❌ 値検証や形式がロジック中にベタ書き
→ ✅ 値オブジェクトに移動し、明示的な意図を設計する
❌ DBレコード = モデルとし、ロジックが薄くなる
→ ✅ “モデル”は業務の振る舞いを担う構造であるべき
結語
オブジェクトとは、“フィールドとメソッドの集まり”ではない。
それは、**“意味を持ち、業務の振る舞いを制御する構造化された知識”**である。
- エンティティに状態と振る舞いを閉じ
- 値オブジェクトで構文と意味を一体化し
- ユースケースはあくまで“操作するだけ”にとどめる
ドメインを設計するとは、現実の複雑さに名前を与え、責任を分けることである。
それがコードを意味あるものに変える第一歩である。