DTOとエンティティの変換コードをfluentなワンライナーで書いてみる

| 3 min read
Author: toshio-ogiwara toshio-ogiwaraの画像

昨今のJavaアプリケーションではレイヤーアーキテクチャにしろ、Clean Architectureにしろ、DTOとエンティティを変換するコードが多く登場します。今回は冗長となりがちなDTOとエンティティの変換コードをラムダとインタフェースを使ってfluent(流暢)なワンライナーで書けるようにする小ネタを紹介します。

変更前のちょっと残念な変換コード

#

外部からの入力などをDTOで受け取り、それをエンティティに変換してサービスに渡す。逆にサービスから取得したエンティティを変換し外部へDTOとして出力するといった次のようなコードを皆さんも目にされたことがあると思います。

public PersonDto getPerson(int id) {
// 取得したエンティティをDTOに変換し結果として返却
return convertToDto(service.getPerson(id));
}
public PersonDto updatePerson(PersonDto dto) {
// DTOをエンティティに変換して更新
var updatedEntity = service.updatePerson(convertToEntity(dto));
// 更新後のエンティティをDTOに変換して結果として返却
return convertToDto(updatedEntity);
}

private PersonDto convertToDto(Person entity) {
return PersonDto.of(entity.getId(), entity.getName());
}
private Person convertToEntity(PersonDto dto) {
return Person.of(dto.getId(), dto.getName());
}

呼び出した結果を変換する処理を素直に実装すると、例のようにカッコを書いてその中にメソッド呼び出しを書いて・・となり、コードを書くリズムがなにか良くないんだよなぁと思われたことはないでしょうか?また評価の順も右に行ってから左に行ってという感じとなり、直観的に理解もしずらいと感じたことはないでしょうか?

fluentなワンライナーなコードへの改善

#

自然に読め、また流れるような(fluentな)感じでリズム良く書けるようにコードは左から右にドットで繋げて書いていきたいですよね。ということで、今回はこのコードをラムダ(メソッド参照)とインタフェースを使ってfluentな実装に変更してみます。

まずはDTOとエンティティの変換ですが、その変換知識はDTO自身が知っていればよいので、変換実装は次のようにDTOに持って行きます。(実装はLombokを利用した例となっています)

@Getter
@AllArgsConstructor(staticName = "of")
static class PersonDto {
private int id;
private String name;
Person toEntity() {
return Person.of(this.id, this.name);
}
static PersonDto toDto(Person source) {
return PersonDto.of(source.getId(), source.getName());
}
}

次にfluentな気持ちでコードをどう読みたいかというと「サービスから取得したエンティティをDTOに変換する」な感じにしたいので、エンティティ自体に「XXXに変換」する責務を持たせる次のインタフェースを導出します。

public interface Transformable {
@SuppressWarnings("unchecked")
default <T, R> R transform(Function<T, R> func) {
return func.apply((T) this);
}
}

そして導出したインタフェースをPersonエンティティにimplementsします。

@Getter
@AllArgsConstructor(staticName = "of")
static class Person implements Transformable {
private int id;
private String name;
}

これで準備は整ったので元のコードをTransformableインタフェースとメソッド参照を使って、fluentなワンライナーな実装にしてみます。

public PersonDto getPerson(int id) {
return service.getPerson(id).transform(PersonDto::toDto);
}
public PersonDto updatePerson(PersonDto dto) {
return service.updatePerson(dto.toEntity()).transform(PersonDto::toDto);
}

冒頭でも触れましたが、昨今のJavaのアプリケーションではDTOとエンティティの変換のようなコードは至る所にでてきます。今回説明したように変換コードはDTO自体に、そして変換処理はインタフェースを切った上で変換対象であるエンティティに持たせることで、処理のまとまり(凝集性)もよくなり、またコードも読みやすくなります。

サンプルコードの全量はGitHubに格納してあります。

豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 基本から理解するJWTとJWT認証の仕組み (2022-12-08)
  2. AWS認定資格を12個すべて取得したので勉強したことなどをまとめます (2022-12-12)
  3. Nuxt3入門(第4回) - Nuxtのルーティングを理解する (2022-10-09)
  4. Viteベースの高速テスティングフレームワークVitestを使ってみる (2022-12-28)
  5. Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する (2022-10-28)
  6. ORマッパーのTypeORMをTypeScriptで使う (2022-07-27)
  7. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)
  8. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  9. GitHub Actions - 構成変数(環境変数)が外部設定できるようになったので用途を整理する (2023-01-16)
  10. Nuxt3入門(第7回) - Nuxt3のプラグイン・ミドルウェアを使う (2022-10-23)