こんにちは。
数ある記事の中から、当記事に目を通していただき、ありがとうございます。
今回は、私がシステムの新規開発をするうえで取り入れるようになった設計手法、
「ドメイン駆動設計」の画期的なツール、値オブジェクトの魅力をお話しします。
ドメイン駆動設計(DDD)の本質とは?
突然ですが、ドメイン駆動設計(DDD)の本質ってなんでしょうか?
色々な難しい説明がありますが、シンプルに表現すると、
「役割を明確にして、それぞれが本当にやるべきことに集中できる仕組み」
だと私は思っています。
これを身体で例えるとすごく分かりやすいです。
手は、手の役割に徹する。
爪は、爪を役割に徹する。
手が爪の仕事までしなくて良いし、爪も手の仕事をする必要がない。
そうやって、自分の本分を徹底することこそがDDDというわけです。
値オブジェクトという便利な道具
この役割を明確にするための強力な道具が、値オブジェクトです。
値オブジェクトを栄養に例えてみます。
爪を生成する細胞は、主にアミノ酸とミネラルの特定の栄養素だけを受け取ります。
余計な栄養素が混ざると、
爪を作る細胞は毎回「これ使える?」と確認が必要になりますよね。
しかし、必要な栄養素しか来ないならその確認は不要。
爪を作ることに集中できるわけです。
つまり値オブジェクトを使うと、各パーツが自分の役割に集中できます。
値オブジェクトの特徴
値オブジェクトの特徴は2つです。
- 状態が変化しない(不変性)
- 値の等価性(内容が同じなら同じオブジェクト扱い)
つまり、あらかじめ決められた安全なデータしか存在しない仕組みなんですね。
具体的にJavaのコードに落とし込んでみます。
import lombok.Value;
import lombok.NonNull;
import java.util.Set;
@Value
public class AminoAcid {
@NonNull String name;
public AminoAcid(String name) {
if (name.isBlank()) {
throw new IllegalArgumentException("アミノ酸名は空にできません。");
}
this.name = name;
}
}
@Value
public class Mineral {
/** 必要なミネラル */
private static final Set<String> VALID_MINERALS = Set.of("カルシウム", "亜鉛", "鉄");
@NonNull String type;
public Mineral(String type) {
if (type.isBlank()) {
throw new IllegalArgumentException("ミネラルの種類は空にできません。");
}
if (!VALID_MINERALS.contains(type)) {
throw new IllegalArgumentException("無効なミネラルの種類です。使用可能なミネラルは: " + VALID_MINERALS);
}
this.type = type;
}
}
@Value
public class Nail {
@NonNull AminoAcid aminoAcid;
@NonNull Mineral mineral;
public void grow() {
System.out.println("爪を生成します: " + aminoAcid.getName() + ", " + mineral.getType());
}
}
爪クラス(Nail)は受け取った栄養(値オブジェクト)が正しいものだと分かっているので、
例えばgrow()
を使うときに余計な確認は必要ありません。
値オブジェクトを使うべき場面
値オブジェクトを使うべき場面は以下の通りです。
- 変更が発生しないデータ
- 条件が明確で固定的なデータ
- 複数箇所で同じ内容が必要なデータ
こういった場面で値オブジェクトを導入すると、
コードが簡潔になり、バグも減って保守性が高まります。
(つまりほぼすべての項目です)
最後に
DDDの本質は、「役割分担を明確にして本質的な仕事に集中する」こと。
値オブジェクトは、その仕組みを作る強力な味方です。
個人的には、DDD以外でも導入したら保守性が上がり、
システムの理解も進めやすい素晴らしいツールになるのではと感じています。
読んでいただき、ありがとうございました!