AIによる要約
@Serviceと@Repositoryは、どちらも@Componentを土台にしたステレオタイプで、コンポーネントスキャンによりBean登録されます。違いは主に役割の表現です。業務処理はService、DBなどの永続化アクセスはRepository、どちらにも当てはまらない共通部品はComponentを基本にします。Repositoryにはデータアクセス例外変換の対象になる意味もあるため、全部をComponentへ統一しない方が設計意図を伝えられます。


Springのクラスへ@Component、@Service、@Repositoryのどれを付けるかは、新人がレビューでよく質問されるポイントです。実際、いずれもSpringのBeanとして検出され、コンストラクタDIできます。
しかし、同じように動くことと、同じ意味であることは別です。業務システムでは、障害調査や改修で初めて見るクラスを短時間で読むため、アノテーションも設計上の案内になります。
この記事のポイント
- 3つともコンポーネントスキャンでBean登録できる
- 業務のユースケースや判断は
@Serviceを基本にする - 永続化やデータアクセスは
@Repositoryを基本にする - 汎用部品や補助クラスは
@Componentを検討する - Spring DataのRepositoryインターフェースは通常アノテーション不要
3つともSpringのBeanとして登録される
@Component
public class CsvLineParser {
}
@Service
public class OrderService {
}
@Repository
public class OrderQueryRepository {
}
@Serviceと@Repositoryには、内部的に@Componentが付いています。そのため、Spring Bootのコンポーネントスキャン対象パッケージにあればBeanとして登録されます。
ここだけを見ると「全部@Componentでよい」と感じますが、専用アノテーションにはクラスの役割を伝える意味があります。フレームワークの追加処理やAOPの適用対象として利用される場合もあります。
@Serviceは業務処理の入口に付ける
@Service
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
public void place(OrderCommand command) {
inventoryService.reserve(
command.productId(),
command.quantity());
Order order = Order.create(command);
orderRepository.save(order);
}
}
Serviceには、注文する、承認する、解約するなど、利用者や業務の目的に対応する処理を置きます。複数Repositoryや外部サービスを組み合わせ、トランザクションの境界になることも多くあります。
単に「何か処理しているクラス」へ全部Serviceを付けるのではありません。日付の書式変換、CSVの1行解析、ハッシュ計算のように業務ユースケースではない補助処理なら、Componentや普通のJavaクラスの方が自然です。
@Repositoryはデータアクセスに付ける
@Repository
public class OrderQueryRepository {
private final JdbcTemplate jdbcTemplate;
public List<OrderSummary> findSummaries(
LocalDate from,
LocalDate to) {
// SQLを実行して検索結果を返す
return List.of();
}
}
Repositoryは、DB、JPA、JDBCなどの永続化技術を隠し、保存・検索・削除を担当します。Serviceから見ると「注文を保存する」「期間内の注文を探す」というインターフェースになります。
@Repositoryが付いたクラスは、対応する仕組みが有効な環境では、JPAなどの技術固有例外をSpringのDataAccessException体系へ変換する対象になります。役割表示だけでなく、データアクセス層として扱う意味があります。
例外変換を目的に何でも@Repositoryへしない
外部API Clientやファイル出力クラスは、DBへアクセスしていないならRepositoryとは限りません。技術的な効果だけでなく、業務上どの境界を表すクラスかを優先します。
Spring DataのRepositoryインターフェースは通常アノテーション不要
public interface OrderRepository
extends JpaRepository<Order, Long> {
List<Order> findByStatus(OrderStatus status);
}
Spring Data JPAが実装を生成するRepositoryインターフェースには、通常@Repositoryを手動で付けなくても検出されます。付けても意味が重複することが多いため、既存プロジェクトの規約へ揃えます。
一方、JdbcTemplateやEntityManagerを使って自分で実装する検索クラスには、@Repositoryを付けると役割が明確です。
@Componentはどこで使うか
@Component
public class OrderCsvParser {
public OrderRow parse(String line) {
// CSV文字列をOrderRowへ変換する
return new OrderRow();
}
}
Componentは汎用的なステレオタイプです。ServiceやRepositoryの役割には当てはまらないが、設定値や別BeanをDIしたい補助クラスで使います。
- CSV・固定長ファイルのParser
- テンプレートを使うメール本文Builder
- アプリ固有のID採番器
- 複数クラスから使う変換・検証部品
- バッチ処理の補助クラス
ただし、依存を持たない単純な計算クラスまで必ずBeanにする必要はありません。普通にnewして使える値オブジェクトや純粋関数的なクラスは、Springへ登録しない選択もあります。
判断基準を表で整理する
| 質問 | 候補 |
|---|---|
| 業務のユースケースや判断を担当するか | @Service |
| DBなどの永続化先へアクセスするか | @Repository |
| どちらでもないアプリ共通部品か | @Component |
| 設定を作るメソッド群か | @Configuration + @Beanを検討 |
| 状態を持つデータそのものか | Bean登録せず普通のクラスを検討 |
「DIしたいから何を付けるか」ではなく、「このクラスは何の責任を持つか」からアノテーションを選びます。
全部@Componentにした場合の困りごと
@Component
public class OrderManager {
// DB検索
// 在庫判定
// メール送信
// CSV出力
}
このクラス名とアノテーションだけでは、業務処理の入口なのか、データアクセスなのか、補助部品なのか判断できません。さらにManagerという曖昧な名前が重なると、改修時に責務を追うコストが増えます。
アノテーションを変えるだけで巨大クラスが直るわけではありませんが、Service・Repository・Componentを分ける検討が、責務整理のきっかけになります。
コンポーネントスキャンされない原因
正しいアノテーションを付けてもBeanが見つからない場合、パッケージ位置を確認します。Spring Bootは通常、@SpringBootApplicationがあるパッケージとその配下をスキャンします。
com.example.app ├── Application.java // @SpringBootApplication ├── controller ├── service └── repository // 通常はスキャン対象 com.example.shared └── parser // app配下でなければ対象外の可能性
対象外パッケージを使うなら@ComponentScanなどの設定を検討します。ただし、設定を増やす前に、アプリケーションの基点パッケージを上位へ置けないか確認します。
同じ型のBeanが複数ある場合
public interface NotificationSender {
void send(String message);
}
@Component
public class MailNotificationSender
implements NotificationSender {
}
@Component
public class SlackNotificationSender
implements NotificationSender {
}
NotificationSenderが2つあるため、型だけでは注入先を決められません。この問題は@Serviceへ変えても解決しません。@Qualifierや@Primary、または用途別インターフェースを検討します。
テストでも役割を意識する
| 対象 | 主なテスト観点 |
|---|---|
| Service | 業務分岐、Repository・外部Clientとの連携 |
| Repository | SQL、JPAマッピング、DB制約 |
| Component | 変換・解析・計算の入出力 |
役割が分かれていれば、Repositoryのテストへ業務判断を詰め込む、Serviceの単体テストで実DBまで毎回起動するといった混乱を避けやすくなります。
現場レビューでよくある指摘
// レビューコメント例 注文確定というユースケースを担当しているため、 @Componentではなく@Serviceで役割を示してください。 // レビューコメント例 JdbcTemplateでDB検索を行うクラスなので@Repositoryが適切です。 データアクセス例外の扱いも既存Repositoryへ揃えてください。 // レビューコメント例 このFormatterは依存を持たない単純な変換クラスです。 Spring Beanにする必要があるか、普通のJavaクラスでよいか確認してください。
提出前のセルフチェック
- クラスの責務を一文で説明できるか
- 業務ユースケースなら@Serviceを選んだか
- 永続化アクセスなら@Repositoryを選んだか
- 汎用部品なら@Componentまたは普通のクラスを検討したか
- Spring Dataインターフェースへ不要な@Repositoryを足していないか
- コンポーネントスキャン対象パッケージにあるか
- 同じ型のBeanが複数ないか
Springの役割分担を体系的に学ぶ参考書
アノテーションの暗記ではなく、DI、AOP、トランザクション、データアクセス層までつなげて学ぶと、クラスの責務から判断できるようになります。
Spring徹底入門 第2版
Spring Frameworkの主要機能を、実務レベルで整理する。
DI、MVC、データアクセス、テスト、セキュリティまで幅広く確認できます。ピンポイント検索から体系学習へ進みたい人向けです。
- Springの全体像を整理したい
- 現場で使う機能を体系的に学びたい
当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。
この記事とあわせて読みたい
まとめ
@Component、@Service、@RepositoryはいずれもBean登録できますが、表す役割が違います。業務処理はService、永続化アクセスはRepository、その他の共通部品はComponentを基本にします。
動くアノテーションを選ぶのではなく、クラスの責務を一文で説明してから選びます。Spring Data Repositoryの扱い、コンポーネントスキャン、複数Beanまで確認し、アノテーションと実装内容を一致させましょう。
