値オブジェクトとは?
値オブジェクトは、ビジネス上の重要な概念(メールアドレス、住所、金額など)を専用のクラスとして表現する設計パターンです。 主にオブジェクト指向プログラミングやドメイン駆動設計(DDD)で使われる重要な概念です。現実世界のアナロジー
値オブジェクトを理解するために、3 つの具体例を見てみましょう!- メールアドレス:
user@example.comは単なる文字列ではなく、特定の形式を持つ連絡先情報 - 住所:
東京都渋谷区1-1-1は都道府県、市区町村、番地が組み合わさった場所の識別子 - 金額:
1,000円は数値の 1,000 と通貨の JPY が組み合わさった価値を表現したもの
値オブジェクトを用いないで実装した場合の問題
値オブジェクトを用いない従来のプログラミングでは、これらの概念を基本型(文字列、数値)で表現しがちです。なぜ問題なのか?
メールアドレスの場合
住所の場合
金額の場合
値オブジェクトを用いない場合に起こり得る問題のまとめ
- バリデーション不足: 不正な値(不正なメール形式など)を防げない
- 不変性の欠如: 予期しない値の変更により、他の部分に影響を与えるリスク
- 意味が不明確:
1000が何を表すのかわからない(円?ドル?) - 型安全性の欠如: 異なる種類の値を誤って混ぜてしまうリスク(円とドルの足し算など)
値オブジェクトによる解決のイメージ
値オブジェクトを使うことで、これらの問題をこう解決できます。メールアドレスの場合
住所の場合
金額の場合
そもそも、値オブジェクトの「値」とは何か?
値オブジェクトにおける「値」とは、初期化時に渡される引数の組み合わせのことです。メールアドレスの場合
住所の場合
金額の場合
値オブジェクトの 3 つの特性
1. 不変性(変更できない)
一度作成されると、内容を変更することができません。2. 値による等価性: 同じ値なら同じオブジェクト
3. 自己完結性: 振る舞いとデータが一体化
値オブジェクトは、そのデータに関連するすべての処理(バリデーション、計算、変換など)を自分自身の中に持っています。他のオブジェクトには依存せず、外部に処理を委譲することもなく、それ単体で完結しているため「自己完結性」と呼びます。値オブジェクトの実装方法
メールアドレスの値オブジェクト化
問題:文字列でメールアドレスを管理
- 不正なメールアドレス形式を設定できる
- 正規化されない(大文字小文字の統一がされない)
- メールアドレスとしての妥当性チェックがない
解決方法:値オブジェクトとして実装
-
バリデーション: 不正な形式を作成時に防ぐ
-
正規化: 大文字小文字を統一
-
型安全性: 文字列ではなく専用の型として扱える
-
一貫した状態: 作成後に内部状態が変更されないため、常に有効な状態を保つ
-
不変性の保証:
freezeにより作成後の変更を防ぐ
練習問題
(1) 住所の値オブジェクト化
問題:文字列で住所を管理
- 値が後から書き変わってしまう
- 不正な値(空文字など)を防げない
解決方法:値オブジェクトとして実装
- バリデーション: 不正なデータを作成時に防ぐ
- 不変性: 一度作成されたオブジェクトは変更できない
- 明確な意味: コードを読むだけで住所であることがわかる
- 複数属性の管理: 関連する複数の値を一つのオブジェクトで安全に管理
- 構造化された比較: 全要素を考慮した等価性判定
(2) 金額の値オブジェクト化
問題:数値で金額を管理
- 通貨が不明で、円なのかドルなのか区別できない
- 異なる通貨同士を誤って計算してしまうリスク
- 金額の計算ロジックが散在し、一貫性がない
解決方法:値オブジェクトとして実装
異なる通貨の混合や不正な金額を防ぐための値オブジェクトです。まとめ
値オブジェクトは、ビジネス上重要な概念を安全で明確な形で表現するための強力なパターンです。バグを防ぐ3つの原則
1. 不変性(Immutability)
一度作成されたら内容を変更できない原則です。これにより以下のメリットが得られます:- 予期しない値の変更によるバグを防ぐ: オブジェクトの状態が途中で変わることがないため、意図しない副作用が発生しません
- 複数の場所で同じオブジェクトを安心して共有できる: 他の処理で値が変更される心配がありません
- スレッドセーフになる: 並行処理でも安全に使用できます
2. 値による等価性(Value Equality)
同じ値を持つオブジェクトは等しいとみなす原則です。これにより以下のメリットが得られます:- オブジェクトの比較が直感的になる: 内容が同じなら等しいという自然な振る舞い
- 意図しない参照の比較によるバグを防ぐ: オブジェクトのIDではなく値で比較されます
- テストが書きやすくなる: 期待値との比較がシンプルになります
3. 自己完結性(Self-contained)
データとそれに関連する振る舞いを一体化する原則です。これにより以下のメリットが得られます:- バリデーションロジックの散在を防ぐ: 検証処理が値オブジェクト内に集約されます
- 不正な状態のオブジェクトが作られることを防ぐ: コンストラクタで必ず検証されます
- ビジネスロジックが一箇所に集約される: 重複や矛盾を防ぎ、保守性が向上します
参考資料
- Effective Use of Value Objects in Domain-Driven Design for Richer Domain Models
- ドメイン駆動設計における値オブジェクトの効果的な使い方についての解説
- Martin Fowler - Value Objects
- ソフトウェアアーキテクチャーの権威、Martin Fowlerによる値オブジェクトの解説
- Domain-Driven Design: Distinguish Entities, Value Objects
- 「あるものがエンティティか値オブジェクトかはドメインに依存する」という視点から、値オブジェクトとエンティティの違いを解説