Spring BootのDTO設計:適切な切り分け方(新人エンジニア向け)
こんにちは。ゆうせいです。
Spring Boot でWebアプリケーションを作るとき、
「DTO(Data Transfer Object)はどうやって設計すればいいの?」 という疑問を持つことがありますよね。
✅ 「DTOは何のために必要?」
✅ 「どこまでDTOを作るべき?」
✅ 「DTOをどう切り分ければ、メンテナンスしやすい設計になる?」
結論:
DTOは「データの受け渡し専用」にして、エンティティ(DBモデル)とは分離するのが基本!
今回は、新人エンジニア向けに、Spring BootのDTOの適切な設計方法 をわかりやすく解説します!
1. DTO(Data Transfer Object)とは?
DTO(データ転送オブジェクト)は、レイヤー間(Controller ⇔ Service ⇔ Repository)でデータをやりとりするためのオブジェクト です。
📌 DTOを使う理由
- エンティティ(DBモデル)とAPIレスポンスを分離できる
- セキュリティ(パスワードなどの機密情報を隠せる)
- APIのリクエストやレスポンスの形式を統一できる
- 拡張性が高まり、メンテナンスしやすくなる
2. DTOを作らないとどうなる?(悪い例)
DTOを使わずに、エンティティ(DBモデル)を直接APIのレスポンスに使う のは、一般的に避けるべきです。
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
private String password; // 🔴 パスワードがAPIレスポンスに含まれてしまう!
}
@RestController
@RequestMapping("/users")
public class UserController {
private final UserRepository userRepository;
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { // 🔴 エンティティをそのまま返してしまう
return userRepository.findById(id).orElseThrow();
}
}
❌ この設計の問題点
- セキュリティの問題 → パスワードなどの機密情報がそのまま返る可能性がある
- APIの仕様変更に弱い → DBの構造が変わると、フロントエンドにも影響が出る
- データ転送量が無駄に増える → 必要のない情報までAPIレスポンスに含まれる
📌 エンティティ(DBモデル)とAPIレスポンスを分離するために、DTOを使うべき!
3. DTOの適切な設計
✅ 基本的なDTOの分け方
DTOの種類 | 役割 |
---|---|
RequestDTO(入力用) | クライアントからのリクエストデータを受け取る |
ResponseDTO(出力用) | APIのレスポンスデータを整形して返す |
内部DTO(Service間のデータ受け渡し) | Service ⇔ Repository の間で使う |
✅ DTOを適切に設計した例
1. ユーザー情報のDTO
public class UserDTO {
private Long id;
private String username;
private String email;
public UserDTO(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
// ゲッター・セッター
}
📌 パスワードなどの機密情報は含めない!
2. APIリクエスト用のDTO(UserRequestDTO)
public class UserRequestDTO {
private String username;
private String email;
private String password;
// ゲッター・セッター
}
📌 クライアント(フロントエンド)からの入力データ用
3. APIレスポンス用のDTO(UserResponseDTO)
public class UserResponseDTO {
private Long id;
private String username;
private String email;
public UserResponseDTO(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.email = user.getEmail();
}
// ゲッターのみ(レスポンスなので、書き換え不可)
}
📌 エンティティとは分離し、APIのレスポンス専用にする
4. DTOをコントローラーとサービスでどのように使うか?
✅ 正しいDTOの使い方(Service層を挟む)
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public UserResponseDTO getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public void createUser(@RequestBody @Valid UserRequestDTO userRequest) {
userService.createUser(userRequest);
}
}
📌 コントローラーでは、DTOを受け渡しに使い、エンティティは扱わない!
✅ Service 層でDTOを変換
@Service
public class UserService {
private final UserRepository userRepository;
public UserResponseDTO getUserById(Long id) {
User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
return new UserResponseDTO(user);
}
public void createUser(UserRequestDTO userRequest) {
User user = new User();
user.setUsername(userRequest.getUsername());
user.setEmail(userRequest.getEmail());
user.setPassword(userRequest.getPassword()); // 🔴 ここで暗号化するのがベスト
userRepository.save(user);
}
}
📌 DTOをエンティティに変換し、データをやりとりする!
5. まとめ
設計のポイント | 説明 |
---|---|
DTOはエンティティと分離する | DBの変更がAPIレスポンスに影響しないようにする |
DTOをリクエストとレスポンスで分ける | UserRequestDTO (入力用) / UserResponseDTO (出力用) |
DTOを使ってセキュリティを強化 | パスワードや機密情報をレスポンスに含めない |
DTOはコントローラーで使い、エンティティはサービスで使う | @Service でDTOをエンティティに変換する |
6. 結論
🚀 DTOは「データの受け渡し専用」にして、エンティティ(DBモデル)とは分離!
🚀 リクエストDTO(入力用)とレスポンスDTO(出力用)を分けると、APIが整理される!
🚀 コントローラーでDTOを使い、エンティティは @Service
で扱う!
この設計を守れば、「セキュリティが強く、メンテナンスしやすいSpring Bootアプリ」 を作れます!
ぜひ実践してみてください!
セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。
投稿者プロフィール

- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。