ドメイン駆動設計（DDD）における値オブジェクトと不変オブジェクト

ソフトウェア開発において、ドメイン駆動設計（DDD）は非常に強力なアプローチです。DDDでは、ビジネスドメインを中心にシステムを設計し、複雑なビジネスロジックをシンプルで一貫性のあるコードに分解します。本記事では、DDDの設計パターンの一つである「値オブジェクト（Value Object）」と「不変オブジェクト（Immutable Object）」の使用について説明します。

### 全体コード構造

まずは、全体のコード構造を見てみましょう。以下のコードは、`Membership` クラスとそれに関連する複数の値オブジェクトを示しています。

```java
@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` などの値オブジェクト

```java
@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` であり、一度設定された後は変更されることがありません。

```java
@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` というファクトリーメソッドを通じて生成されます。これにより、オブジェクト生成の一貫性が保たれ、ビジネスロジックが適切にカプセル化されます。

```java
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）** として扱うことができます。アグリゲートは、一貫した状態を持つオブジェクト群を管理し、トランザクション単位で操作を行うための設計パターンです。

### 使用例

```java
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）の重要な概念である「値オブジェクト」および「不変オブジェクト」に基づいています。これにより、システムのビジネスロジックを安定的に管理し、オブジェクトの状態が常に正しいことを保証します。また、ファクトリーメソッドとアグリゲートパターンを活用することで、コードの一貫性と可読性も向上しています。