ソフトウェア開発において、ドメイン駆動設計(DDD)は非常に強力なアプローチです。DDDでは、ビジネスドメインを中心にシステムを設計し、複雑なビジネスロジックをシンプルで一貫性のあるコードに分解します。本記事では、DDDの設計パターンの一つである「値オブジェクト(Value Object)」と「不変オブジェクト(Immutable Object)」の使用について説明します。
全体コード構造
まずは、全体のコード構造を見てみましょう。以下のコードは、Membership
クラスとそれに関連する複数の値オブジェクトを示しています。
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Membership {
@Getter private final String membershipId;
@Getter private final String name;
@Getter private final String email;
@Getter private final String address;
@Getter private final boolean isValid;
@Getter private final String aggregateIdentifier;
public static Membership generateMember(
MembershipId membershipId,
MembershipName membershipName,
MembershipEmail membershipEmail,
MembershipAddress membershipAddress,
MembershipIsValid membershipIsValid,
MembershipAggregateIdentifier membershipAggregateIdentifier
) {
return new Membership(
membershipId.membershipId,
membershipName.nameValue,
membershipEmail.emailValue,
membershipAddress.addressValue,
membershipIsValid.isValidValue,
membershipAggregateIdentifier.aggregateIdentifier
);
}
@Value
public static class MembershipId {
String membershipId;
public MembershipId(String value) {
this.membershipId = value;
}
}
@Value
public static class MembershipName {
String nameValue;
public MembershipName(String value) {
this.nameValue = value;
}
}
@Value
public static class MembershipEmail {
String emailValue;
public MembershipEmail(String value) {
this.emailValue = value;
}
}
@Value
public static class MembershipAddress {
String addressValue;
public MembershipAddress(String value) {
this.addressValue = value;
}
}
@Value
public static class MembershipIsValid {
boolean isValidValue;
public MembershipIsValid(boolean value) {
this.isValidValue = value;
}
}
@Value
public static class MembershipAggregateIdentifier {
String aggregateIdentifier;
public MembershipAggregateIdentifier(String value) {
this.aggregateIdentifier = value;
}
}
}
このコードは、顧客の「会員情報」を表す Membership
クラスと、その属性を保持するいくつかの値オブジェクトを含んでいます。次に、これらの要素を一つずつ説明します。
1. 値オブジェクト(Value Object)とは
値オブジェクトは、識別子を持たず(Entityとは違う点!)、主にその属性(値)に意味があるオブジェクトです。例えば、顧客の「名前」や「メールアドレス」などが該当します。
MembershipId
, MembershipName
, MembershipEmail
などの値オブジェクト
@Value
public static class MembershipId {
String membershipId;
public MembershipId(String value) {
this.membershipId = value;
}
}
この MembershipId
クラスは顧客の「会員ID」を表す値オブジェクトです。@Value
アノテーションにより、このクラスは不変オブジェクトとなり、membershipId
フィールドは変更できません。
同様に、MembershipName
, MembershipEmail
, MembershipAddress
などのクラスも値オブジェクトとして設計されています。これらのクラスは、顧客の各属性を保持するために使用されます。
2. 不変オブジェクト(Immutable Object)とは
不変オブジェクトとは、一度設定された状態を変更できないオブジェクトのことです。Javaでは、クラスのフィールドを final
として宣言することで、不変オブジェクトを実現します。
Membership
クラスの不変性
Membership
クラスも不変オブジェクトとして設計されています。すべてのフィールドは private final
であり、一度設定された後は変更されることがありません。
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Membership {
@Getter private final String membershipId;
@Getter private final String name;
@Getter private final String email;
@Getter private final String address;
@Getter private final boolean isValid;
@Getter private final String aggregateIdentifier;
}
この設計により、Membership
クラスのオブジェクトは一度作成されると、その状態が変更されることはありません。
3. ファクトリーメソッド(Factory Method)パターン
Membership
オブジェクトは、直接インスタンス化するのではなく、generateMember
というファクトリーメソッドを通じて生成されます。これにより、オブジェクト生成の一貫性が保たれ、ビジネスロジックが適切にカプセル化されます。
public static Membership generateMember(
MembershipId membershipId, MembershipName membershipName, MembershipEmail membershipEmail,
MembershipAddress membershipAddress, MembershipIsValid membershipIsValid,
MembershipAggregateIdentifier membershipAggregateIdentifier) {
return new Membership(
membershipId.membershipId,
membershipName.nameValue,
membershipEmail.emailValue,
membershipAddress.addressValue,
membershipIsValid.isValidValue,
membershipAggregateIdentifier.aggregateIdentifier
);
}
4. アグリゲート(Aggregate)パターン
Membership
クラスは、複数の値オブジェクトを含んでおり、これらの関連するオブジェクトをグループ化しています。このようなクラスは アグリゲート(Aggregate) として扱うことができます。アグリゲートは、一貫した状態を持つオブジェクト群を管理し、トランザクション単位で操作を行うための設計パターンです。
使用例
Membership.MembershipId membershipId = new Membership.MembershipId("M001");
Membership.MembershipName membershipName = new Membership.MembershipName("John Doe");
Membership.MembershipEmail membershipEmail = new Membership.MembershipEmail("john@example.com");
Membership.MembershipAddress membershipAddress = new Membership.MembershipAddress("kotoku Street, Tokyo");
Membership.MembershipIsValid membershipIsValid = new Membership.MembershipIsValid(true);
Membership.MembershipAggregateIdentifier membershipAggregateIdentifier =
new Membership.MembershipAggregateIdentifier("AGG-001");
Membership membership = Membership.generateMember(
membershipId,
membershipName,
membershipEmail,
membershipAddress,
membershipIsValid,
membershipAggregateIdentifier
);
結論
この設計は、ドメイン駆動設計(DDD)の重要な概念である「値オブジェクト」および「不変オブジェクト」に基づいています。これにより、システムのビジネスロジックを安定的に管理し、オブジェクトの状態が常に正しいことを保証します。また、ファクトリーメソッドとアグリゲートパターンを活用することで、コードの一貫性と可読性も向上しています。