mcpField
B
best-java-structure

best-java-structure

github.com/chasepassion開発139 回インストール1 週間前に更新
リポジトリ

best-java-structure について

best-java-structureは、Spring BootとMyBatis-Plusを使うJavaチーム向けのレイヤードアーキテクチャ設計ガイドです。新規開発、リファクタリング、レビューに向けて、五層の責務や依存方向、データ転送の規則を説明します。

主な機能

  1. 01五層の責務を定義
  2. 02一方向依存とインターフェース抽象化
  3. 03DTO、VO、Entityの受け渡し方
  4. 04例外処理とトランザクション境界
  5. 05レイヤー間呼び出しの注意点

活用シーン

  1. 01新しいJava Webプロジェクトの構成設計
  2. 02マルチモジュールJavaシステムの改修
  3. 03コードレビューでの設計規約確認
  4. 04チームのレイヤー開発標準の策定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/chasepassion/skills --skill best-java-structure

インストール後、Claude Code で/best-java-structureを入力して読み込まれたか確認します。

詳細

Java レイヤードアーキテクチャ設計パターン(Spring Boot + MyBatis-Plus)

Spring Boot と MyBatis-Plus を使って、従来の5層アーキテクチャを実装するための実践ガイドです。

適用する場面

次のようなシナリオで、このスキルを使用します。

  • 新しい Java Web プロジェクトを作成するとき
  • 既存のプロジェクトアーキテクチャをリファクタリングするとき
  • マルチモジュールの Java システムを設計するとき
  • コードレビューでアーキテクチャへの準拠を確認するとき
  • チーム開発のアーキテクチャ標準を定めるとき
  • データベースや技術スタックを移行するとき

前提条件

  • Spring Boot 3.x
  • データアクセスに MyBatis-Plus を使用
  • API 境界で DTO と VO を分離

クイックリファレンス

優先度カテゴリ影響範囲プレフィックス
1関心の分離アーキテクチャの明確さ-principle-
2一方向の依存関係依存関係の管理-dependency-
3インターフェースの抽象化置き換え可能性-abstraction-
4データ転送API の一貫性-data-
5例外処理エラー処理-exception-
6レイヤー間の呼び出しアーキテクチャへの準拠-crosslayer-
7逆方向の依存関係アーキテクチャへの準拠-reverse-
8エンティティの公開セキュリティ-entity-exposure-

基本原則

1. 関心の分離(SoC)

各レイヤーは1種類の問題を解決し、他のレイヤーに属する責務を担ってはなりません。

// Correct: clear responsibilities per layer
// Presentation layer: HTTP handling only
@RestController
public class ProjectController {
    private final ProjectService projectService;
    public ProjectController(ProjectService projectService) {
        this.projectService = projectService;
    }
    @GetMapping("/{id}")
    public ApiReturn<ProjectVo> getProject(@PathVariable Long id) {
        Project project = projectService.getProject(id);
        ProjectVo vo = new ProjectVo();
        BeanUtils.copyProperties(project, vo);
        return ApiReturn.of(vo);
    }
}
// Business logic layer: business rules only
@Service
public class ProjectService {
    private final ProjectMapper projectMapper;
    public ProjectService(ProjectMapper projectMapper) {
        this.projectMapper = projectMapper;
    }
    public Project getProject(Long id) {
        Project project = projectMapper.selectById(id);
        if (project == null) {
            throw new BusinessException(404, "Project does not exist");
        }
        return project;
    }
}
// Data access layer: data interfaces only
public interface ProjectMapper extends BaseMapper<Project> {
}
2. 一方向の依存関係

上位レイヤーは下位レイヤーを呼び出せますが、下位レイヤーは上位レイヤーに依存してはなりません。

// Correct: depend on a lower layer abstraction
@Service
public class ProjectService {
    private final ProjectMapper projectMapper;
    public ProjectService(ProjectMapper projectMapper) {
        this.projectMapper = projectMapper;
    }
}
// Incorrect: cross-layer invocation
@RestController
public class ProjectController {
    private final ProjectMapper projectMapper;
    public ProjectController(ProjectMapper projectMapper) {
        this.projectMapper = projectMapper;
    }
}
3. インターフェースの抽象化

レイヤー間のやり取りには、具象実装ではなくインターフェースを使用します。

// Correct: mapper interface managed by MyBatis-Plus
public interface ProjectMapper extends BaseMapper<Project> {
}
// Incorrect: depending on a non-existent implementation class
public class ProjectService {
    private ProjectMapperImpl projectMapperImpl;
}

アーキテクチャのレイヤー

従来の5層アーキテクチャ
Presentation Layer → Business Logic Layer → Data Access Layer → Persistence Layer → Database Layer
レイヤーSpring のスタック一般的な名称主な責務
プレゼンテーション層@ControllerController、APIHTTP リクエストとレスポンスを処理する
ビジネスロジック層@ServiceServiceビジネスルールとワークフローを実装する
データアクセス層@MapperMapper、DAODB アクセスインターフェースを定義する
永続化層MyBatis XML、EntityMapper XML、EntitySQL マッピングとエンティティ定義
データベース層MySQLDatabaseデータを保存および取得する

レイヤーの定義

プレゼンテーション層

責務:

  • HTTP リクエストとレスポンスの処理
  • パラメーターの受け取りと検証
  • ルーティングとディスパッチ
  • データ形式の変換(DTO ↔ Entity ↔ VO)
  • 例外処理と HTTP ステータスのマッピング テンプレート:
@RestController
@RequestMapping("/api/projects")
public class ProjectController {
    private final ProjectService projectService;
    public ProjectController(ProjectService projectService) {
        this.projectService = projectService;
    }
    @GetMapping("/{id}")
    public ApiReturn<ProjectVo> getProject(@PathVariable Long id) {
        Project project = projectService.getProject(id);
        ProjectVo vo = new ProjectVo();
        BeanUtils.copyProperties(project, vo);
        return ApiReturn.of(vo);
    }
    @PostMapping
    public ApiReturn<ProjectVo> createProject(@Valid @RequestBody ProjectCreateRequest request) {
        Project project = new Project();
        BeanUtils.copyProperties(request, project);
        Project created = projectService.createProject(project);
        ProjectVo vo = new ProjectVo();
        BeanUtils.copyProperties(created, vo);
        return ApiReturn.of(vo);
    }
}

設計上の注意点:

  • 薄く保つ:HTTP 関連のロジックのみを扱います
  • データ転送には DTO と VO を使用します
  • Bean Validation とグローバル例外処理で検証を一元化します
  • ビジネスロジックを含めません
  • データベースに直接アクセスしません
ビジネスロジック層

責務:

  • ビジネスルールとワークフローの実装
  • データ検証とビジネスチェック
  • 認可とアクセス制御
  • トランザクション管理(境界はこのレイヤーに置きます)
  • 複数のサービスやマッパーの調整
  • データアクセス層の呼び出し テンプレート:
@Service
public class ProjectService {
    private final ProjectMapper projectMapper;
    public ProjectService(ProjectMapper projectMapper) {
        this.projectMapper = projectMapper;
    }
    public Project getProject(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(400, "Invalid project ID");
        }
        Project project = projectMapper.selectById(id);
        if (project == null) {
            throw new BusinessException(404, "Project does not exist");
        }
        return project;
    }
    @Transactional
    public Project createProject(Project project) {
        if (project.getCustomerId() == null) {
            throw new BusinessException(400, "Customer ID must not be null");
        }
        project.setCreatedTime(LocalDateTime.now());
        project.setStatus(ProjectStatus.CREATED);
        projectMapper.insert(project);
        return project;
    }
}

設計上の注意点:

  • コアとなるビジネスロジックを含みます
  • トランザクションには @Transactional を使用します
  • ビジネス上の検証を実行します
  • 複数のサービスやマッパーを統括します
  • HTTP に関する処理を含めません
  • DB 接続の処理を直接行いません
  • Service は DTO を返してはなりません(DTO は入力モデルです)。Entity/Domain または VO を一貫して返します。
データアクセス層(MyBatis-Plus)

責務:

  • DB アクセスインターフェースを定義します
  • CRUD の抽象化を提供します
  • 永続化の詳細をカプセル化します
  • 必要に応じて集約クエリ用インターフェースを提供します テンプレート:
public interface ProjectMapper extends BaseMapper<Project> {
}

設計上の注意点:

  • インターフェースのみとし、具体的な実装は含めません
  • メソッド名から意図が明確に伝わるようにします
  • SQL 文は記述しません(複雑な SQL には XML を使用します)
  • ビジネスロジックは含めません
  • トランザクション管理は行いません
  • Service 層に依存してはなりません
永続化層

責務:

  • SQL マッピングと結果マッピング
  • エンティティの定義
  • データベース固有の SQL 断片(必要な場合) テンプレート(MyBatis-Plus XML):
<mapper namespace="com.example.mapper.ProjectMapper">
    <select id="selectActive" resultType="com.example.entity.Project">
        SELECT * FROM project WHERE status = 'CREATED'
    </select>
</mapper>

設計上の注意点:

  • SQL の作成と最適化に重点を置きます
  • 結果マッピングと型変換を行います
  • 再利用性を高めるために SQL 断片を使用します
  • ビジネスロジックは含めません
  • ビジネス上の検証は行いません
  • 接続管理とトランザクションの境界は Spring と DataSource が処理します
データベース層

責務:

  • 永続ストレージ
  • データ整合性制約
  • インデックスとパフォーマンスの最適化
  • トランザクションのサポート テンプレート(MySQL):
CREATE TABLE project (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    customer_id BIGINT NOT NULL,
    status VARCHAR(50) DEFAULT 'CREATED'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

データ転送パターン

DTO、VO、Entity の使用
Frontend → Controller → Service → Mapper → Database
           DTO        Entity    Entity
           VO         Entity
変換例
@PostMapping("/projects")
public ApiReturn<ProjectVo> createProject(@Valid @RequestBody ProjectCreateRequest request) {
    Project project = new Project();
    BeanUtils.copyProperties(request, project);
    Project created = projectService.createProject(project);
    ProjectVo vo = new ProjectVo();
    BeanUtils.copyProperties(created, vo);
    return ApiReturn.of(vo);
}

例外処理

一元化された例外処理
public class BusinessException extends RuntimeException {
    private final int code;
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }
    public int getCode() {
        return code;
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public ApiReturn<Void> handleBusinessException(BusinessException e) {
        return ApiReturn.error(e.getCode(), e.getMessage());
    }
    @ExceptionHandler(Exception.class)
    public ApiReturn<Void> handleException(Exception e) {
        return ApiReturn.error(500, "Internal server error");
    }
}

トランザクションのガイドライン

  • トランザクションの境界は Service メソッドに設定します。
  • 読み取り専用クエリには @Transactional(readOnly = true) を使用します。
  • デフォルトでは RuntimeException でロールバックします。必要に応じて、チェック例外でのロールバックを文書化します。
  • Controller、Mapper、XML ではトランザクションを使用しないでください。

よくある落とし穴

落とし穴 1:レイヤーをまたぐ呼び出し
// Incorrect: Controller directly calls Mapper
@RestController
public class ProjectController {
    private final ProjectMapper projectMapper;
    public ProjectController(ProjectMapper projectMapper) {
        this.projectMapper = projectMapper;
    }
    public List<Project> getProjects() {
        return projectMapper.selectList(null);
    }
}
落とし穴 2:依存関係の逆転
// Incorrect: Service depends on Controller
@Service
public class ProjectService {
    private ProjectController projectController;
}
落とし穴 3:SQL 内のビジネスロジック

<select id="getById" resultMap="BaseResultMap">
    SELECT * FROM project
    WHERE id = #{id}
      AND is_deleted = 0
      AND has_permission(#{currentUserId}, id) = 1
</select>
落とし穴 4:Controller から Entity を直接返す
// Incorrect: Controller returns Entity directly
@GetMapping("/projects/{id}")
public Project getProject(@PathVariable Long id) {
    return projectService.getProject(id);
}

プロジェクト構成テンプレート(推奨)

src/main/java/com/xdyai/backend/
  controller/
    internal/
    request/
  service/
    common/
    context/
    integration/
  mapper/
  entity/
    enumeration/

src/main/resources/
  application.yml
  application-dev.yml
  mapper/
  db/
  validation/
  integration/

ベストプラクティスのまとめ

プレゼンテーション層
  • 薄く保ちます:HTTP ロジックのみを扱います
  • データ転送には DTO/VO を使用します
  • 例外処理を一元化します
  • ビジネスロジックは含めません
  • Mapper に直接アクセスしません
ビジネスロジック層
  • 中核となるビジネスロジックを含みます
  • トランザクション管理には @Transactional を使用します
  • 複数のサービスを調整します
  • HTTP に関する処理は行いません
  • データベース接続を直接管理しません
データアクセス層
  • インターフェースのみとします
  • メソッド名を明確にします
  • MyBatis-Plus を介して CRUD を行います
  • SQL 文は記述しません
  • ビジネスロジックは含めません
永続化層
  • SQL の作成に重点を置きます
  • SQL 断片を使用します
  • 複雑なクエリには動的 SQL を使用します
  • ビジネスロジックは含めません
  • ビジネス上の検証は行いません
レイヤー間の通信
  • Controller ← DTO
  • Service ← Entity
  • Mapper ← Entity
  • Controller ← VO
  • レイヤーをまたぐ呼び出しを避けます
  • 依存関係の逆転を避けます

コメント