こんにちは!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アプリケーションを開発できるはずです。