エンティティの詰め替えは自動化したいですよね。

現代のソフトウェア開発において、複雑なビジネスロジックを扱う際に、設計パターンが非常に重要になってきます。その中でも、ドメイン駆動設計(DDD)は、ドメインの複雑さをうまく取り扱うための強力な手法として広く知られています。

今回は、Javaを使用して、DDDの設計原則に基づいたユーザー情報の登録処理をChatGPTに実装してもらいましょう。この記事では、アプリケーション層、ドメイン層、およびインフラ層という3つの境界に焦点を当てて、DTOとエンティティのやりとりを行います。

ユーザー情報

  • ユーザー名(username)
  • メールアドレス(email)

今回のユーザー情報の登録処理では、以下の2つの属性を持つとします。

DDDの設計

ドメイン駆動設計では、システムを複数の層に分割します。この記事では、以下の3つの層を採用します。

アプリケーション層

この層では、外部からのリクエストを受け取り、内部のドメインモデルに適した形に変換します。また、レスポンスを生成するために、ドメインモデルを外部に適した形に変換します。

ドメイン層

この層では、ビジネスロジックが中心となります。ドメインエンティティ、値オブジェクト、ドメインサービスなど、ドメインのコア概念がここで定義されます。

インフラ層

この層は、データの永続化や外部システムとの通信など、システムの外部とのインタラクションを扱います。

それでは具体的な実装をChatGPTに聞いていきましょう。


プロンプト

JAVAプログラミングにDDDの設計という文脈で、ユーザー情報の登録処理を考えたいです。次のような3つの境界でDTO、エンティティをやりとりする場合のベストプラクティスに関してプログラマーを演じて考えて。DTO、エンティティの実装および、詰め替え(convert処理) と、ユーザー登録処理のソースコードを完全な形で生成してください。

## レイヤー
### アプリケーション層
- swagger-api の HTTP POST Body 
### ドメイン層
- ユーザー情報のエンティティ
### インフラ層
- DBのテーブルにMappingされたJPAのEntity

## User の属性情報
- String username;
- String email;

ChatGPTの回答

これらのレイヤーをうまく組み合わせてJavaでのユーザー情報の登録処理を考えてみましょう。

アプリケーション層

この層では、APIの入力として受け取った情報を内部のドメインモデルに適した形に変換します。DTO(Data Transfer Object)がここで活用されます。

UserDTO.java

javaCopy codepackage com.example.application.dto;

public class UserDTO {
    private String username;
    private String email;

    public UserDTO() {
    }

    public UserDTO(String username, String email) {
        this.username = username;
        this.email = email;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

ドメイン層

この層では、ビジネスロジックが扱うエンティティを定義します。

User.java

javaCopy codepackage com.example.domain;

public class User {
    private String username;
    private String email;

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // Getters and possibly domain-specific methods
}

インフラ層

この層では、JPAエンティティを用いてデータベースとのマッピングを行います。

UserEntity.java

javaCopy codepackage com.example.infrastructure;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String email;

    protected UserEntity() {}

    public UserEntity(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // Getters and Setters
}

コンバータークラス

これらのクラス間の変換を行うためのヘルパークラスを作成します。

UserConverter.java

javaCopy codepackage com.example.application.converter;

import com.example.application.dto.UserDTO;
import com.example.domain.User;
import com.example.infrastructure.UserEntity;

public class UserConverter {
    
    public static User dtoToDomain(UserDTO dto) {
        return new User(dto.getUsername(), dto.getEmail());
    }
    
    public static UserEntity domainToEntity(User domainUser) {
        return new UserEntity(domainUser.getUsername(), domainUser.getEmail());
    }
}

ユーザー登録処理

これらを組み合わせてユーザーの登録処理を行うサービスクラスを作成します。

UserService.java

javaCopy codepackage com.example.application.service;

import com.example.application.converter.UserConverter;
import com.example.application.dto.UserDTO;
import com.example.domain.User;
import com.example.infrastructure.UserEntity;
import com.example.infrastructure.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void registerUser(UserDTO userDto) {
        // Convert DTO to Domain
        User domainUser = UserConverter.dtoToDomain(userDto);

        // Convert Domain to Entity
        UserEntity userEntity = UserConverter.domainToEntity(domainUser);

        // Save to Database
        userRepository.save(userEntity);
    }
}

UserRepository.java

javaCopy codepackage com.example.infrastructure;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<UserEntity, Long> {
}

そして最終的にコントローラーを使用してHTTPリクエストを受け取ります。

UserController.java

javaCopy codepackage com.example.application.controller;

import com.example.application.dto.UserDTO;
import com.example.application.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping
    public void registerUser(@RequestBody UserDTO userDto) {
        userService.registerUser(userDto);
    }
}

これにより、DDDの設計を採用してJavaでユーザー情報の登録処理を実装することができます。ただし、実際のプロジェクトでは、エラーハンドリング、バリデーション、ログ記録など、さらなる要件が考慮される必要があります。


DDDを採用することで、ビジネスロジックをより効果的にモデル化し、メンテナンス性の高いアプリケーションを構築することができます。この記事ではJavaを使って、DDDの基本的な概念に従ったユーザー情報の登録処理を実装しました。