Java DTO and Domain に対する整理

javaのドメインモデルとdtoを開発しながら問題になる部分を整理しました

## 1 . DTO とドメインなぜ *可変*／*不変* を分けるのか？

| 階層                | 推奨する性質                                                        | 理由                                        |
| ----------------- | ------------------------------------------------------------- | ----------------------------------------- |
| **DTO（転送オブジェクト）** | **可変**でも問題なし<br>― デフォルトコンストラクター＋setter が Jackson／JPA と最も相性が良い | 直列化・逆直列化専用。ビジネスロジックを持たないため、可変でも整合性リスクが小さい |
| **ドメインモデル**       | **不変**（`record` や `final` フィールド）                              | 生成時にのみ整合性を検証 → 一貫性保証／スレッド安全／テスト容易         |

> **まとめ** ― 不変性は *ビジネスルールを抱えるレイヤ* にだけ適用すれば十分。

---

## 2. Jackson 逆直列化 3 パターン

| パターン                                   | コード例                                                                     | 特徴                                                      |
| -------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------- |
| **① デフォルトコンストラクター＋setter**             | `java @NoArgsConstructor class Foo { String name; void setName(...); } ` | 実装が最も簡単。`final` フィールドには使えない                             |
| **② `@JsonCreator` + `@JsonProperty`** | `java @JsonCreator Foo(@JsonProperty("name") String n)`                  | 完全不変 DTO が可能。全パラメータに `@JsonProperty` 必須                 |
| **③ `record`（Java 14+）**               | `java public record Foo(String name,int value) {} `                      | 生成子・getter 自動生成、`final` フィールド OK。Jackson 2.12+ なら追加設定なし |

> `-parameters` オプションを付ければ（`@JsonProperty` なしでも）通常のコンストラクター名推論が可能。

---

## 3 . 実践コード例

### 3-1. 転送 DTO ― `TemperatureSensorDto`（可変）

```java
@Getter @Setter @NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = false)   // additionalProperties=false を反映
public class TemperatureSensorDto {
    private String  name;
    private Double  value;
    private String  unit;   // "Celsius" | "Fahrenheit" | "Kelvin"
}
```

### 3-2. ドメインオブジェクト ― `TemperatureSensor`（不変）

```java
public record TemperatureSensor(String name,
                                double value,
                                Unit unit) {
    public TemperatureSensor {
        if (name == null || name.isBlank()) throw new IllegalArgumentException("name");
        if (unit == null) throw new IllegalArgumentException("unit");
    }
    public enum Unit { CELSIUS, FAHRENHEIT, KELVIN }
}
```

### 3-3. マッパー（MapStruct）

```java
@Mapper(componentModel = "spring")
public interface SensorMapper {
    TemperatureSensor toDomain(TemperatureSensorDto dto);
    TemperatureSensorDto toDto(TemperatureSensor domain);
}
```

---

## 4 . レイヤーごとの責務整理

```
┌─ REST / Kafka / GraphQL ──────────────────────────────────────────┐
│  TemperatureSensorDto  （可変） ← JSON 直列化／逆直列化         │
└─────────────▲  Mapper  ▼────────────────────────────────────────┘
┌─ Service / Domain ───────────────────────────────┐
│  TemperatureSensor（record・不変）               │
│  ビジネスロジック・整合性保証                    │
└─────────────────────────────────────────────────┘
```

* DTO を変更してもドメインロジックへの影響は最小限
* `record` により「不変 + 生成時検証」をシンプルに実現

---

## 5 . チェックリスト

| チェック項目                  | ポイント                                                                                   |
| ----------------------- | -------------------------------------------------------------------------------------- |
| DTO に `final` フィールドはある？ | setter パターンなら `final` を避ける。不変 DTO が欲しければ `record` + `-parameters` か `@JsonCreator` を使う |
| `@JsonCreator` 利用時      | 各パラメータに `@JsonProperty` を付与し、クラスにつき 1 個にする                                             |
| JSON の `"enum"`         | Java 予約語なのでフィールド名を変更し、`@JsonProperty("enum")` でマッピング                                   |
| 追加フィールドの拒否              | `@JsonIgnoreProperties(ignoreUnknown = false)` で `additionalProperties:false` を反映      |
| `record` サポートバージョン      | Jackson 2.12 以上 & JDK 14 以上が必要                                                         |