こんにちは!Web開発における三層構造、一度は学んだものの、いざ実装しようとすると
「あれ、どこに何を記述すればいいんだっけ?」と手が止まってしまうこと、ありますよね。
私もまさにその状態でした。
冗長なコードを解消し、より保守性の高いアプリケーションを目指すべく、三層構造に再挑戦した過程を共有したいと思います。
三層構造とは?
Webアプリケーションを、役割ごとに「プレゼンテーション層」「アプリケーション層」「データ層」の3つに分割する設計モデルです。
各層を独立させることで、システムの保守性、拡張性、再利用性が向上します。
プレゼンテーション層 (Presentation Layer)
- ユーザーインターフェース (UI) を担当し、ユーザーとの直接的なやり取りを行います。
- HTML, CSS, JavaScript など、フロントエンド技術が用いられます。
- ユーザーからのリクエストをアプリケーション層へ送信し、アプリケーション層からの応答をユーザーに表示します。
アプリケーション層 (Application Layer)
- アプリケーションの主要な処理 (ビジネスロジック) を担当します。
- プレゼンテーション層からのリクエストに基づき、データの処理、計算、検証などを行います。
- データ層と連携し、データの検索、作成、更新、削除を行います。
- Java, Python, PHP など、サーバーサイドのプログラミング言語が用いられます。
データ層 (Data Layer)
- データの保存と管理を担当します。
- データベースやファイルシステムなどが含まれます。
- アプリケーション層からの要求に応じて、データの読み書きを行います。
三層構造を図で表すと
┌──────────────┐
│ プレゼンテーション層 │←ユーザー
└──────┬──────┘
│
┌──────┴──────┐
│ アプリケーション層 │
└──────┬──────┘
│
┌──────┴──────┐
│ データ層 │
└──────────────┘
実際のコード例と解説
今回、ポモドーロタイマーアプリケーションを例に、各層の実装を見ていきましょう。
プレゼンテーション層 (PomodoroController.java)
Java
package com.example.pomodorotimer.controller;
import com.example.pomodorotimer.service.PomodoroService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PomodoroController {
private final PomodoroService pomodoroService;
@Autowired
public PomodoroController(PomodoroService pomodoroService) {
this.pomodoroService = pomodoroService;
}
@PostMapping("/pomodoro")
public String startPomodoro(@RequestParam int workMinutes, @RequestParam int workSeconds,
@RequestParam int breakMinutes, @RequestParam int breakSeconds) {
int workTotalSeconds = workMinutes * 60 + workSeconds;
int breakTotalSeconds = breakMinutes * 60 + breakSeconds;
pomodoroService.startPomodoro(workTotalSeconds, breakTotalSeconds);
return "ポモドーロタイマーを開始しました。";
}
}
@RestController
: JSON や文字列などのレスポンスを返します。@Autowired
: Spring の依存性注入 (DI) により、PomodoroService のインスタンスが自動的に注入されます。DI により、クラス間の依存関係が疎になり、コードの保守性・柔軟性が向上します。@RequestParam
: HTTP リクエストのパラメータをメソッドの引数にマッピングします。例えば、/pomodoro?workMinutes=25&workSeconds=0&breakMinutes=5&breakSeconds=0
というリクエストの場合、workMinutes
には 25,workSeconds
には 0, ... がそれぞれ渡されます。
アプリケーション層 (TimerService.java, PomodoroService.java)
Java
// TimerService.java
package com.example.pomodorotimer.service;
import java.util.Timer;
import java.util.TimerTask;
import org.springframework.stereotype.Service;
@Service
public class TimerService {
public void startCountdown(int taskSeconds, Runnable onFinish) {
// ... (タイマー処理の実装)
}
}
// PomodoroService.java
package com.example.pomodorotimer.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PomodoroService {
private final TimerService timerService;
// ... (ポモドーロタイマーのロジック)
}
@Service
: アプリケーション層の処理を記述します。ここでは、タイマー機能とポモドーロタイマーのロジックが実装されています。
データ層 (Task.java, TaskRepository.java)
Java
// Task.java
package com.example.pomodorotimer.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import java.time.LocalDateTime;
@Entity
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ... (タスクの属性)
}
// TaskRepository.java
package com.example.pomodorotimer.repository;
import com.example.pomodorotimer.domain.Task;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TaskRepository extends JpaRepository<Task, Long> {
}
@Entity
: データベースのテーブルに対応するエンティティを宣言します。@Id
,@GeneratedValue
: エンティティの ID を定義します。@Repository
: エンティティを操作するためのインターフェースを宣言します。
まとめ
三層構造を意識することで、コードの見通しが良くなり、保守性・拡張性が向上することを実感しました。
最初は戸惑うかもしれませんが、各層の役割を理解し、適切な場所にコードを記述することで、より堅牢なWebアプリケーションを開発できるはずです。