カテゴリ: Quarkus 更新日: 2026/03/08

Quarkusプロジェクトのレイヤー構造を完全理解!API・Service・Repositoryを初心者向けにやさしく解説

Quarkusプロジェクトのレイヤー構造を学ぼう(API, Service, Repository)
Quarkusプロジェクトのレイヤー構造を学ぼう(API, Service, Repository)

先生と生徒の会話形式で理解しよう

生徒

「Quarkusでアプリを作り始めたんですが、APIとかServiceとかRepositoryとか、フォルダをどう分ければいいのか分かりません…」

先生

「Quarkusでも、JavaのWebアプリではレイヤー構造を意識するのが基本になります。」

生徒

「とりあえず全部同じ場所に書くのは良くないですよね?」

先生

「役割ごとに分けることで、コードが読みやすくなり、保守もしやすくなります。順番に見ていきましょう。」

1. Quarkusにおけるレイヤー構造とは

1. Quarkusにおけるレイヤー構造とは
1. Quarkusにおけるレイヤー構造とは

Quarkusにおけるレイヤー構造とは、「役割ごとにクラスの置き場所を分けて整理する考え方」のことです。アプリの中身を一か所にまとめて書くのではなく、仕事の内容ごとに担当を分けるイメージを持つと理解しやすくなります。

具体的には、画面や外部からの呼び出しを受け取るAPI層、処理の流れや判断を行うService層、データを扱うRepository層の三つに分ける構成がよく使われます。これにより、「どこに何を書くコードなのか」が自然と分かるようになります。

プログラミング未経験者でも、この三層構造を最初に意識しておくことで、コードがごちゃごちゃになりにくく、後から読み返したときも理解しやすくなります。Quarkusを学ぶ最初の一歩として、とても重要な考え方です。


package org.example;

/*
 アプリ全体を「役割」で分けるイメージ例
 ・api        :外からのリクエストを受け取る
 ・service    :やりたい処理をまとめる
 ・repository :データを扱う
*/
public class LayerImageExample {
    // このクラス自体に処理は書かず、
    // 役割ごとに別のクラスへ分けていく
}

2. APIレイヤーの役割

2. APIレイヤーの役割
2. APIレイヤーの役割

APIレイヤーは、アプリケーションの「受付窓口」のような存在です。Webブラウザやスマートフォン、他のシステムから送られてきたリクエスト(お願い)を最初に受け取り、結果をレスポンスとして返します。Quarkusでは、このAPIレイヤーをREST APIとして実装するのが一般的で、HTTP通信を担当します。

ここで重要なのは、APIレイヤーではできるだけシンプルな処理だけを行うという考え方です。例えば、URLやHTTPメソッドを受け取り、必要な情報をServiceレイヤーに渡すまでが役割です。計算や業務ロジックなどの複雑な処理は行わず、専門のServiceレイヤーに任せます。

APIレイヤーを薄く保つことで、「どこで何をしているのか」が分かりやすくなり、プログラミング初心者でもコードの流れを理解しやすくなります。また、修正やテストもしやすくなり、実務でもよく使われる設計になります。

まずは「APIレイヤー=リクエストを受けて、Serviceに処理をお願いし、結果を返す場所」と覚えておくとよいでしょう。


package org.example.api;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.example.service.HelloService;

@Path("/hello")
public class HelloResource {

    @Inject
    HelloService helloService;

    @GET
    public String hello() {
        // Serviceレイヤーに処理をお願いして、結果をそのまま返す
        return helloService.message();
    }
}

このサンプルでは、「/hello」というURLにアクセスすると、HelloResourceクラスが呼び出されます。@GETは「GETメソッドでアクセスされたときに実行される」という意味です。

中身を見ると、APIレイヤー自身では難しい処理はしていません。HelloServicemessage()メソッドを呼び出し、その戻り値をそのまま返しています。これが、APIレイヤーをシンプルに保つ基本的な書き方です。

プログラミング未経験の方は、「APIレイヤーは指示を受けて、担当者(Service)につなぐ係」とイメージすると理解しやすいでしょう。

3. Serviceレイヤーの役割

3. Serviceレイヤーの役割
3. Serviceレイヤーの役割

Serviceレイヤーは、アプリケーションの中心となる処理を担当します。ビジネスロジックや処理の流れをまとめる場所です。

APIレイヤーから呼び出され、必要に応じてRepositoryレイヤーを利用します。複数の処理を組み合わせるのも、このServiceレイヤーの役割です。

Serviceに処理を集約することで、APIとデータアクセスを分離でき、コードの再利用性も高まります。


package org.example.service;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class HelloService {

    public String message() {
        return "Hello Quarkus Layered Architecture";
    }
}

4. Repositoryレイヤーの役割

4. Repositoryレイヤーの役割
4. Repositoryレイヤーの役割

Repositoryレイヤーは、データベースとのやり取りを担当します。データの取得、保存、更新、削除といった処理をまとめる場所です。

Quarkusでは、Hibernate ORMやPanacheなどを利用してRepositoryを実装することが一般的です。Serviceレイヤーから呼び出されることで、データ操作を行います。

データアクセス処理をRepositoryに集約することで、ServiceやAPIがデータベースの詳細を意識せずに済みます。


package org.example.repository;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class SampleRepository {

    public String findMessage() {
        return "Database Message";
    }
}

5. レイヤー構造を分けるメリット

5. レイヤー構造を分けるメリット
5. レイヤー構造を分けるメリット

Quarkusプロジェクトでレイヤー構造を採用する最大のメリットは、責任が明確になることです。どこに何を書くべきか迷いにくくなります。

また、修正や機能追加の影響範囲が限定されるため、保守性が向上します。初心者でも、大きなプロジェクト構成を理解しやすくなります。

API、Service、Repositoryを分ける考え方は、他のJavaフレームワークでも共通して使える知識です。

6. よくある初心者のつまずきポイント

6. よくある初心者のつまずきポイント
6. よくある初心者のつまずきポイント

初心者がよく悩むのは、「どこまでをServiceに書くか」「APIに処理を書いてしまって良いのか」という点です。

基本的には、APIは受け口、Serviceは処理、Repositoryはデータ操作と役割を固定すると迷いにくくなります。

最初は多少厳密でなくても構いませんが、役割を意識して書く習慣をつけることが大切です。

7. Quarkusとレイヤー構造の相性

7. Quarkusとレイヤー構造の相性
7. Quarkusとレイヤー構造の相性

QuarkusはCDIによる依存性注入が標準で使えるため、レイヤー構造との相性が非常に良いです。

ServiceやRepositoryを注入するだけで、自然な形で層を分離できます。設定も少なく、コードもシンプルに保てます。

この設計を理解しておくことで、Quarkus開発の基礎体力が身につきます。

まとめ

まとめ
まとめ

今回の記事では、Quarkusにおけるレイヤー構造の重要性と、API・Service・Repositoryという三層構造の具体的な役割について深く掘り下げてきました。JavaのモダンなフレームワークであるQuarkusにおいて、なぜこのように役割を分ける必要があるのか、その理由は単なる慣習ではなく、アプリケーションの拡張性やテストのしやすさに直結するからです。

Quarkus開発で押さえておきたい設計のポイント

改めて整理すると、各レイヤーには以下のような責任があります。

  • API層(Resource):外部インターフェース。HTTPリクエストの検証やステータスコードの制御に専念します。
  • Service層:ビジネスロジックの心臓部。複数のエンティティを操作したり、外部連携を行ったりするロジックをここに集約します。
  • Repository層:データアクセスの抽象化。Panacheなどを用いてデータベース操作をカプセル化し、上位層がDBの詳細を知らなくても済むようにします。

これらの階層を意識することで、スパゲッティコード(何でもどこにでも書かれている状態)を防ぎ、チーム開発においても「どこを直せばいいか」が即座に判断できるようになります。QuarkusのDI(依存性注入)機能を活用すれば、これらの層を疎結合に保つことは非常に簡単です。

実践的なサンプルプログラム:ユーザー登録機能の例

ここでは、学んだ三層構造をさらに具体化するために、ユーザー情報を保存するシミュレーションプログラムを紹介します。

1. Repositoryレイヤー(データアクセス)

まずは、ユーザーデータを管理するリポジトリです。ここでは簡単なリスト形式でデータを保持する例を示します。


package org.example.repository;

import jakarta.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;

@ApplicationScoped
public class UserRepository {
    private List<String> users = new ArrayList<>();

    public void save(String username) {
        users.add(username);
        System.out.println("DB保存完了: " + username);
    }

    public List<String> findAll() {
        return users;
    }
}

2. Serviceレイヤー(ビジネスロジック)

次に、ビジネスロジックを扱うサービス層です。重複チェックなどのバリデーションをここで行います。


package org.example.service;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.example.repository.UserRepository;
import java.util.List;

@ApplicationScoped
public class UserService {

    @Inject
    UserRepository userRepository;

    public String registerUser(String name) {
        if (name == null || name.isEmpty()) {
            return "名前が無効です";
        }
        userRepository.save(name);
        return "登録成功: " + name;
    }

    public List<String> getUsers() {
        return userRepository.findAll();
    }
}

3. APIレイヤー(リソース)

最後に、エンドユーザーとの窓口となるAPI層です。


package org.example.api;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import org.example.service.UserService;

@Path("/users")
public class UserResource {

    @Inject
    UserService userService;

    @GET
    @Path("/add")
    public String add(@QueryParam("name") String name) {
        return userService.registerUser(name);
    }

    @GET
    public String list() {
        return "登録ユーザー一覧: " + userService.getUsers().toString();
    }
}

4. 実行結果の確認

実際にAPIを叩いた際の、コンソール出力やレスポンスのイメージは以下の通りです。


(ブラウザで /users/add?name=Quarkus-kun にアクセスした場合)
レスポンス: 登録成功: Quarkus-kun
DB保存完了: Quarkus-kun

(続けて /users にアクセスした場合)
レスポンス: 登録ユーザー一覧: [Quarkus-kun]

このように、役割が綺麗に分かれていることがわかりますね。APIが「何を受け取るか」を決め、Serviceが「どう処理するか」を考え、Repositoryが「どう保存するか」を実行する。この黄金律を守ることが、Quarkusエンジニアとしての第一歩です。

先生と生徒の振り返り会話

生徒

「先生、今回の解説で三層構造のイメージがかなり具体的に湧いてきました!APIは受付嬢、Serviceは料理人、Repositoryは倉庫番、みたいな感じでしょうか?」

先生

「おお、その比喩は非常に分かりやすいですね。受付(API)が直接倉庫(DB)に走り出したらパニックになりますから、ちゃんと料理人(Service)にオーダーを出すのが正しい流れです。」

生徒

「これって、規模が小さいアプリでも絶対に分けないといけないんですか?」

先生

「良い質問です。極めて小規模なプロトタイプなら1つのクラスにまとめても動きます。でも、後から機能を追加したくなった時に、結局層を分けることになります。最初からこのテンプレートに沿って書く癖をつけておくと、後々の自分が楽になりますよ。」

生徒

「確かに、後からぐちゃぐちゃのコードを整理するのは大変そうですね…。あと、Quarkusだと@Injectを使うだけで簡単に他の層を呼び出せるのが便利だと感じました。」

先生

「そうですね。Quarkusの強力なDI機能があるからこそ、こうした設計がスマートに実装できるんです。レイヤー間の依存を疎結合に保つことで、テストコードも書きやすくなります。次は、各層の単体テストの書き方についても学んでいきましょうか。」

生徒

「ぜひお願いします!まずはこの三層構造を意識して、自分でも簡単なCRUDアプリを作ってみようと思います!」

関連記事:
カテゴリの一覧へ
新着記事
New1
Quarkus
Quarkus拡張開発をマスター!ビルドプロセスの仕組みと内部構造を徹底解説
New2
Micronaut
Micronautの@Factoryとは?複雑なBean生成を管理するための方法を解説
New3
Quarkus
QuarkusのDIとCDIを完全理解!@Producesでプロデューサーメソッドを使う方法を初心者向けに解説
New4
Java
JavaのStringBufferクラスを徹底解説!スレッド安全な文字列操作の仕組みと使い分け
人気記事
No.1
Java&Spring記事人気No1
Java
Javaのコンパイルと実行の流れを解説!JVM・JDK・JREの違いも初心者向けに整理
No.2
Java&Spring記事人気No2
Micronaut
Micronautのルーティング設定ガイド!プレフィックス付与とAPIバージョニングの基本
No.3
Java&Spring記事人気No3
Micronaut
Micronautのフィルタ徹底解説!HTTPリクエスト共通処理をスマートに追加する方法
No.4
Java&Spring記事人気No4
Quarkus
QuarkusのCI/CD入門!GitHub Actionsで自動デプロイを実現する方法
No.5
Java&Spring記事人気No5
Java
JavaのString比較を徹底解説!equalsと==の違い、初心者が陥る罠とは?
No.6
Java&Spring記事人気No6
Java
Java Functionインタフェースの使い方を完全ガイド!map変換と処理チェーンを理解する
No.7
Java&Spring記事人気No7
Quarkus
Quarkus拡張開発を徹底解説!仕組みから自作エクステンションの作り方まで
No.8
Java&Spring記事人気No8
Quarkus
Quarkus入門!GitHub ActionsでCI/CDパイプラインを構築して自動ビルドを実現する方法