Spring Boot + Reactで記事投稿アプリを作成予定｜DTOを作成してみた

※学習中のため間違っている箇所があるかもしれません。
# はじめに
今回はDTOを実装していきます。
DTOは`Service`と`Controller`間のデータをやり取りするための重要なオブジェクトです。
しっかりと使用方法を覚えていきましょう

# DTOとは
`DTO`とは`Data Transfer Object`の略で、
データを他レイヤーへ転送するためのオブジェクトです。

### DTOとEntityについて
DTOとEntityは構造が似ていても責務が異なるため、
明確に使い分ける必要があります。
ここで`DTO`の責務について簡単に記載します。
- サービス層とプレゼンテーション層のやり取りを行う
- 外部に公開すべき最小限のデータのみ保持する
- Entityに含まれる機密情報を隠蔽し、レイヤー間の責務分離を実現します

つまり、`DTO`は必要最低限のデータを保持し、受け渡す責務を負っています。

一方`Entity`は
- ドメインモデルを表現し、DBとの永続化処理の中心
- Rpository層を介して永続化されたデータの取得、更新、削除を行う

となっており、`Entity`と`DTO`では責務が異なります。
構造が似ている場合があっても、責務と利用範囲が異なるので、明確に理解しておきましょう。

今回は前回のサービス機能をもとに`DTO`を作成していきます。
記載しているのは代表的な`DTO`で構成は他と同じになっているので、
最小限の`DTO`だけ記載させていただきます。

# DTOを作成
以下に今回作成した代表的な`DTO`を記述します。
他の`DTO`でも主な構造は同じなので今回は割愛します。

## 共通結果返却DTO
### ResultDto
共通の結果を返却するための`DTO`です。
主に処理結果やメッセージの返却を行います。
```java:ResultDto.java
import lombok.Builder;
import lombok.Getter;

/***
 * 結果返却DTO
 * Service内での処理結果共有で利用する
 * Controllerからの返却はしない
 */
@Getter
public class ResultDto {
    
    /***
     * 処理結果
     */
    private final boolean result;

    /***
     * メッセージ
     * 成功の場合もメッセージを設定
     */
    private final String message;

    /***
     * 初期設定用コンストラクタ
     * @param result
     * @param message
     */
    @Builder
    public ResultDto(boolean result, String message){
        this.result = result;
        this.message = message;
    }
}
```

## レスポンス用DTO
### GetArticleDetailDto
レスポンス用のDTOです。
DBから取得した値を設定するため、
アノテーションでのバリデーションは行いません。

```java:GetArticleDetailDto.java
import java.time.LocalDateTime;


import lombok.Builder;
import lombok.Getter;

/***
 * 記事詳細表示用DTO
 * 記事の基本的な情報を保持する
 */
@Getter
public class GetArticleDetailDto {

    /***
     * タイトル
     */
    private final String title;

    /***
     * 内容
     */
    private final String content;

    /***
     * 表示フラグ
     */
    private final boolean releaseFlg;

    /***
     * 作成日
     */
    private final LocalDateTime createdAt;

    /***
     * 更新日
     */
    private final LocalDateTime updatedAt;

    /***
     * 初期設定用コンストラクタ
     * @param title
     * @param content
     * @param releaseFlg
     * @param createdAt
     * @param updatedAt
     */
    @Builder
    public GetArticleDetailDto(String title, String content, boolean releaseFlg
    , LocalDateTime createdAt, LocalDateTime updatedAt){
      this.title = title;
      this.content = content;
      this.releaseFlg = releaseFlg;
      this.createdAt = createdAt;
      this.updatedAt = updatedAt;
    }
}
```

## リクエスト用DTO
### InsertArticleDetailDto
リクエスト用のDTOです
受け取った情報をテーブルへ保存します。
```java:InsertArticleDetailDto.java
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import lombok.Getter;

/***
 * 記事投稿用DTO
 * 記事投稿時に必要なデータを保持する
 */
@Getter
public class InsertArticleDetailDto {

    /***
     * ユーザーID
     */
    @Min(0)
    private final int userId;

    /***
     * タイトル
     */
    @NotBlank
    @Size(max=50)
    private final String title;

    /***
     * 内容(本文)
     */
    @NotBlank
    @Size(max=255)
    private final String content;

    /***
     * 公開フラグ
     */
    private final boolean releaseFlg;

    /***
     * 削除フラグ
     */
    private final boolean deleteFlg;

    
    /***
     * 初期設定用コンストラクタ
     * @param userId
     * @param title
     * @param content
     * @param releaseFlg
     * @param deleteFlg
     */
    @Builder
    public InsertArticleDetailDto(int userId, String title, String content, boolean releaseFlg, boolean deleteFlg){
      this.userId = userId;
      this.title = title;
      this.content = content;
      this.releaseFlg = releaseFlg;
      this.deleteFlg = deleteFlg;
    }
}
```

## 削除用DTO
記事削除用のDTOです。
削除フラグを更新する際に使用します。
### UpdateUserDeleteFlgDto

```java:UpdateUserDeleteFlgDto.java
import lombok.Builder;
import lombok.Getter;

/***
 * ユーザー削除用DTO
 * 削除フラグを保持する
 */
@Getter
public class UpdateUserDeleteFlgDto {

    /***
     * 削除フラグ
     */
    private final boolean deleteFlg;

    /***
     * 初期設定用コンストラクタ
     * @param deleteFlg
     */
    @Builder
    public UpdateUserDeleteFlgDto(boolean deleteFlg){
        this.deleteFlg = deleteFlg;
    }
}
```
## バリデーションに関して
`DTO`はデータの受け渡し用クラスです。
そのためバリデーションは基本的なものだけを使用し、
詳細な処理は`Service`層で行う予定です。

基本的にバリデーションは`Entity`ではなく`DTO`や`Service`で行うべきだと考えています。

## アノテーションの説明
### `@Builder`
`@Builder`をコンストラクタまたはメソッドに付与することで、
以下のように直感的な形式でインスタンス生成ができるようになります。

```java
InsertArticleDetailDto.builder()
  .userId(ユーザーID)
  .title(タイトル)
  .content(内容)
  .releaseFlg(false)
  .deleteFlg(false)
  .build();
```

また、今回は使用していませんが、フィールドに`List`や`Set`などコレクション型を使用している場合
`@Singular`を付与することにより要素を1件ずつ追加する形式のビルダーメソッドが生成されます。

```java
public class User {
    String name;
    String email;

    @Singular("content")
    List<String> contents;
}
```
```java
public class Test {
    User user = User.builder()
        .name("モス・バーガー")
        .email("MosLike@gmail.com")
        .content("コンテンツ1")
        .content("コンテンツ2");
}
```

このように記述することで、引数の順序を気にせず読みやすくなります。
フィールドの増減があってもコードの修正は最小限で済むので
拡張性、保守性の高い設計が可能です。


# まとめ
今回は`DTO`の解説をしました。
`DTO`と`Entity`の違いを明確にして、有効に使用できるようにしていきましょう。

次回以降
- `Service`,`Controller`
- `React`連携

今回の記事が少しでもspring boot初学者の助けになれば幸いです。

[前：Spring Boot + Reactで記事投稿アプリを作成予定｜Flyway入門：DBマイグレーションの基礎からテーブル作成・データ挿入まで解説してみた](https://qiita.com/suica1010/items/5e4c1776b016cc71dca7)