Spring Bootを学習していると、よく登場するのが**MVC(Model-View-Controller)**という言葉です。
Model
View
Controller
の頭文字を取ってMVCと呼ばれています。
しかし、Spring Bootにはさらに、
Controller
Service
Repository
Entity
などのクラスが登場するため、
「MVCって3つじゃなかったの?」
「ServiceやRepositoryはどこに入るの?」
「ModelってEntityのこと?」
と混乱しやすいです。
この記事では、MVCの基本からSpring BootでどのようにMVCが使われているのかまで整理します。
MVCとは?
MVCとは、アプリケーションの役割を大きく次の3つに分ける設計の考え方です。
| 要素 | 役割 |
|---|---|
| Model | データやビジネスロジックを扱う |
| View | 画面を表示する |
| Controller | リクエストを受け取り、ModelとViewをつなぐ |
ざっくり表現すると、
Model → データ・処理担当
View → 画面担当
Controller → 交通整理担当
というイメージです。
Controllerとは?
Controllerは、ユーザーから送られてきたHTTPリクエストを受け取る役割を持っています。
Spring Bootでは、例えば次のように書けます。
@Controller
public class UserController {
@GetMapping("/users")
public String users(Model model) {
model.addAttribute("name", "田中");
return "users";
}
}
ブラウザから、
GET /users
というリクエストが送られると、
@GetMapping("/users")
に対応するメソッドが呼び出されます。
Controllerはリクエストを受け取り、必要な処理を行ったあと、どのViewを表示するのかを決定します。
今回の場合、
return "users";
としているため、usersというViewを表示します。
Viewとは?
Viewは、ユーザーに表示する画面を担当します。
Spring Bootでは、Thymeleafなどのテンプレートエンジンを利用できます。
例えば、
src/main/resources/templates/users.html
に次のHTMLがあるとします。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ユーザー</title>
</head>
<body>
<p th:text="${name}"></p>
</body>
</html>
Controllerでは、
model.addAttribute("name", "田中");
としていました。
そのため、
<p th:text="${name}"></p>
の${name}に田中が入り、ブラウザには、
田中
と表示されます。
つまりViewは、
Controllerから渡されたデータを使って画面を表示する
という役割を持っています。
Modelとは?
ここが少しややこしいポイントです。
MVCにおけるModelは、単純に「Modelという名前のクラス」を意味するわけではありません。
Modelは、
アプリケーションのデータやビジネスロジックを担当する部分
という広い概念です。
Spring Bootでは、例えば次のようなクラスがModel側の処理を担当します。
Service
Repository
Entity
例えば、
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> findAll() {
return userRepository.findAll();
}
}
Serviceではアプリケーションの処理を担当し、
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
Repositoryではデータベースへのアクセスを担当します。
さらに、
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
Entityによってデータを表現します。
これらをまとめて、MVCでは広い意味でModel側として考えることができます。
SpringのModelとMVCのModelは少し意味が違う
Spring Boot初心者が特に混乱しやすいのがここです。
Controllerには、
public String users(Model model)
というコードが登場します。
このModelを見て、
「これがMVCのModelか!」
と思ってしまいがちですが、少し違います。
Spring MVCのModelは、
ControllerからViewへデータを渡すための入れ物
として使われます。
例えば、
model.addAttribute("name", "田中");
とすると、
<p th:text="${name}"></p>
のようにViewからデータを利用できます。
つまり、
MVCのModel
と、
org.springframework.ui.Model
は同じ言葉でも意味が少し違います。
ここは区別して覚えておくと混乱しにくくなります。
Spring BootでのMVCの流れ
例えば、ユーザー一覧画面を表示するとします。
処理の流れを単純化すると、
ブラウザ
|
| GET /users
v
Controller
|
| findAll()
v
Service
|
| findAll()
v
Repository
|
v
Database
データを取得したら、今度は逆方向に戻ってきます。
Database
|
v
Repository
|
v
Service
|
v
Controller
|
| Modelにデータを格納
v
View(Thymeleaf)
|
v
ブラウザ
コードにすると、例えば次のようになります。
@Controller
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users")
public String users(Model model) {
List<User> users = userService.findAll();
model.addAttribute("users", users);
return "users";
}
}
Controllerが直接データベースを操作するのではなく、
Controller
↓
Service
↓
Repository
↓
Database
というように役割を分けています。
なぜMVCに分けるのか?
「全部Controllerに書けば簡単なのでは?」
と思うかもしれません。
小さなアプリケーションなら、それでも動きます。
しかし、Controllerに、
HTTPリクエスト処理
データベースアクセス
業務ロジック
画面表示
入力チェック
などをすべて書いてしまうと、Controllerがどんどん巨大になります。
例えば、
@GetMapping("/users")
public String users(Model model) {
// DBから取得
// データを加工
// 条件判定
// 計算
// Viewへ渡す
return "users";
}
という状態になり、機能が増えるほど管理が難しくなります。
そこで役割を分けます。
Controller
↓
HTTPリクエストの処理
Service
↓
ビジネスロジック
Repository
↓
データベースアクセス
View
↓
画面表示
これを責務の分離と呼びます。
MVCにするメリット
MVCによって役割を分離すると、次のようなメリットがあります。
コードの役割が分かりやすい
例えばDB関連の処理を確認したい場合はRepository、業務ロジックを確認したい場合はServiceを見る、といったようにコードを探しやすくなります。
修正しやすい
役割が分かれているため、変更の影響範囲を小さくしやすくなります。
テストしやすい
ServiceやControllerなどを分離していることで、それぞれを個別にテストしやすくなります。
大規模なアプリケーションにも対応しやすい
機能が増えても役割ごとにコードを整理できるため、コードが巨大化するのを防ぎやすくなります。
Spring BootではMVCをさらに細かく分けている
MVCは、
Model
View
Controller
という大きな役割分担です。
Spring Bootのアプリケーションでは、さらに役割を細分化して、
Controller
↓
Service
↓
Repository
↓
Database
という構成をよく使用します。
そのため、
MVC = Controller・Service・Repository
という意味ではありません。
MVCという大きな設計の考え方があり、その中でSpring Bootではさらに責務を細かく分けている、と考えると理解しやすいです。
まとめ
MVCとは、
Model
View
Controller
の3つにアプリケーションの役割を分ける設計の考え方です。
それぞれの役割は、
| 要素 | 主な役割 |
|---|---|
| Model | データ・ビジネスロジック |
| View | 画面表示 |
| Controller | HTTPリクエストを受け取り処理を振り分ける |
となります。
Spring Bootではさらに、
Controller
↓
Service
↓
Repository
↓
Database
のように役割を細かく分けることが一般的です。
また、
org.springframework.ui.Model
はMVCそのもののModelを表しているわけではなく、ControllerからViewへデータを渡すために使われるオブジェクトです。
最初は、
Controller = 受付・交通整理
Service = 処理
Repository = DBアクセス
View = 画面
くらいのイメージから理解していくと、Spring Bootのコード全体の流れが見えやすくなります。