AIによる要約
Spring BootのDTO変換は、必ずController、必ずServiceと決まるものではありません。大事なのは、Entityを外へ直接出さず、画面/API用の形に変換することです。単純な変換はControllerやService、複雑な変換はMapperへ分けると読みやすくなります。


Spring Bootの現場では、Entityをそのまま画面やAPIに返さず、DTOへ変換することが多いです。
しかし、DTO変換をどこに書くかは初学者が迷いやすいポイントです。Controllerに書くのか、Serviceに書くのか、Mapperを作るのか、現場でも方針が分かれます。
この記事のポイント
- Entityを外部向けレスポンスとして直接返さない
- 単純な表示用変換はController寄りでもよい
- 業務判断を含む変換はService寄りで考える
- 複雑な変換や再利用する変換はMapperへ切り出す
まずEntityをそのまま返さない
EntityはDBと関係するクラスです。画面やAPIで返したい項目と、DB上の項目は一致しないことがあります。
@GetMapping("/users/{id}")
public UserEntity getUser(@PathVariable Long id) {
return userService.findEntity(id);
}
この書き方では、Entityの内部構造がAPIに出てしまいます。不要な項目、内部管理項目、リレーションが混ざると、画面側にも影響します。
public class UserResponse {
private Long id;
private String name;
private String email;
}
外部へ返す形は、レスポンスDTOとして明示する方が安全です。
単純な変換ならControllerでも読める
ServiceがEntityを返し、ControllerでレスポンスDTOへ詰め替える方針の現場もあります。変換が単純で、画面/APIの表現に近い場合は読みやすいことがあります。
@GetMapping("/users/{id}")
public UserResponse getUser(@PathVariable Long id) {
User user = userService.findUser(id);
return new UserResponse(user.getId(), user.getName(), user.getEmail());
}
この変換は、取得したUserをAPIレスポンスの形に整えているだけです。Controllerにあっても、処理の意図は大きく崩れていません。
ただし、変換が長くなったり、複数Controllerで同じ変換を使ったりするなら、Mapperへの切り出しを考えます。
業務判断を含むならServiceで扱う
DTO変換の中に、会員ランク、利用可否、表示制御、権限判定のような業務判断が入る場合は、Controllerだけで完結させると責務が混ざります。
public UserDetailResponse findDetail(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
boolean editable = permissionService.canEdit(user);
return new UserDetailResponse(
user.getId(),
user.getName(),
editable
);
}
この例では、編集可能かどうかという業務判断が含まれています。このような場合はService側に置く方が自然です。
複雑ならMapperへ切り出す
項目数が多い、ネストしたDTOがある、複数の場所で同じ変換を使う場合は、Mapperクラスへ切り出すと読みやすくなります。
@Component
public class UserMapper {
public UserResponse toResponse(User user) {
return new UserResponse(
user.getId(),
user.getName(),
user.getEmail()
);
}
}
Mapperを作ると、ControllerやServiceの中に詰め替え処理が広がるのを防げます。特に、画面が増えてDTOが複数になると効果があります。
Request DTOからEntityへ直接詰め替えない
リクエストDTOをそのままEntityへ詰め替える処理にも注意が必要です。画面入力とDB更新は意味が違うからです。
public void update(UserUpdateRequest request) {
User user = userRepository.findById(request.getId()).orElseThrow();
user.setName(request.getName());
user.setEmail(request.getEmail());
userRepository.save(user);
}
この程度なら分かりやすいですが、更新してよい項目、権限、状態チェック、null時の扱いが増えると、単純な詰め替えでは危険です。
更新処理では、DTO変換というより「どの項目を、どの条件で更新してよいか」という業務処理としてServiceで扱います。
どこに書くかの判断基準
DTO変換の置き場所を決める基準
- 表示/APIの形に整えるだけならController寄り
- 業務判断や権限判定を含むならService寄り
- 同じ変換を複数箇所で使うならMapper
- 項目数が多くControllerが読みにくいならMapper
- Entityを外に出すための言い訳としてDTO変換を省略しない
大事なのは、どこに置くかを機械的に決めることではなく、変換の意味を説明できることです。
現場レビューではどう見られるか
// レビューコメント例 // Controller内でEntityを直接返しています。 // APIレスポンスとして不要な項目まで出る可能性があるため、 // UserResponseへ変換してください。 // // 変換処理が複数箇所で使われているので、UserMapperへの切り出しも検討してください。
この指摘は、DTOを作ること自体が目的ではありません。外部に出す情報を制御し、ControllerやServiceを読みやすく保つための指摘です。
レビュー前のセルフチェック
DTO変換の確認リスト
- Entityをそのまま返していないか
- 外部に出してよい項目だけDTOに入っているか
- 変換処理がControllerで長くなりすぎていないか
- 業務判断をControllerの変換処理に混ぜていないか
- 同じ変換が複数箇所に重複していないか
この記事とあわせて読みたい
まとめ
DTO変換の置き場所に絶対の正解はありません。ただし、Entityを直接外へ出さないこと、変換の意味を説明できること、複雑ならMapperへ切り出すことを意識すると、現場でレビューされにくい設計になります。
