3層構造ってなんぞや?MVCのこと?Djangoでなんかやったような。詳しく調べた!

設計について調べた

独学でやってきて、好き勝手にアプリ作ってきたから設計とかあまり意識したことないので、勉強しようと思いました。

教えてChatGPT、Claude,Gemini!!!

3層構造

サーバ構成での3層構造とアプリケーションサーバの3層構造という概念があるらしい

サーバ構成の3層構造は以下の通り

  • プレゼンテーション層・・・Webサーバ

  • アプリケーション層・・・アプリケーションサーバ

  • データ層・・・データベースサーバ

で、このアプリケーションサーバ内をまた3層構造にすると

  • プレゼンテーション層

  • ビジネスロジック層

  • データアクセス層

3層構造って聞いたことあったけど、サーバだけの話じゃなかったのを知らなかった・・・

以下はそれぞれの層の主な役割

サーバ構成の3層構造:

プレゼンテーション層 (Webサーバ): クライアントからのリクエストを受け取り、レスポンスを返す役割を担う。 アプリケーション層 (アプリケーションサーバ): ビジネスロジックを処理する中心的な層 データ層 (データベースサーバ): データの永続化と管理を行う

アプリケーションサーバ内部の3層構造:

プレゼンテーション層: Webサーバからのリクエストを解釈し、適切な形式でレスポンスを生成する ビジネスロジック層: アプリケーションの中核となるロジックを実装する データアクセス層: データベースサーバとのやり取りを抽象化し、上位層にデータを提供する

MVC?

フレームワークでよくみるやつ。
MVCモデル(Model-View-Controller)は、ソフトウェア設計パターンの一つで、アプリケーションを3つの主要なコンポーネントに分離します。
先ほどのアプリケーションサーバ内部の3層構造からまた3層構造に分類。3つに分けるの好きだな・・・

よくある解釈では
モデル(M):ビジネスロジック層およびデータアクセス層に対応
ビュー(V):プレゼンテーション層に対応
コントローラ(C):プレゼンテーション層とビジネスロジック層の橋渡し役

1. モデル(Model)

対応する層:ビジネスロジック層+データアクセス層
役割:
ビジネスロジックの実装:データの検証やビジネスルールを定義します。
データアクセス:データベースや外部データソースとのやり取りを行います。

2. ビュー(View)

対応する層:プレゼンテーション層
役割:
ユーザーインターフェースの表示:データをユーザーに見やすい形で表示します。
UIコンポーネントの構築:HTML、CSS、JavaScriptなどを用いて画面を作成します。

3. コントローラ(Controller)

対応する層:主にプレゼンテーション層(ただし、ビジネスロジック層とも関連)
役割:
ユーザーからの入力処理:リクエストを受け取り、適切なアクションを決定します。
モデルとビューの仲介:モデルからデータを取得し、ビューに渡します。
一部のビジネスロジック:簡単な処理やデータの変換を行うこともあります。

以下は図に表したもの。

Modelがビジネスロジック層とデータアクセス層を兼ねる場合もありますし、データアクセス層にリポジトリ(Repositories)を作成して、モデルとリポジトリを分離することもあります。

Python Djangoでは

Djangoは**MTV(Model-Template-View)**アーキテクチャを採用していますが、これはMVCの概念をDjango流にアレンジしたものです。

Model(モデル):

データベースのテーブルやビジネスロジックを定義(models.py)。
データアクセスとビジネスルールを担当。

Template(テンプレート):

ユーザーに表示するHTMLなどのテンプレート(templates/)。
MVCのViewに相当。

View(ビュー):

リクエストを処理し、モデルから必要なデータを取得してテンプレートに渡す(views.py)。
MVCのControllerに相当。

この組み合わせた構造は、大規模なエンタープライズアプリケーションでよく見られます。各層が明確に分離されることで、以下のような利点がある。

スケーラビリティ: 各層を独立してスケールアップ/アウトできます。
保守性: 各層の責務が明確なので、変更の影響範囲を限定できます。
セキュリティ: 層ごとにセキュリティ対策を実装できます。
柔軟性: 各層の実装技術を独立して選択・変更できます。

ただし、プロジェクトの規模や要件によっては、このような複雑な構造が過剰になる場合もあります。小規模なプロジェクトでは、より簡略化された構造を採用することもあります。重要なのは、プロジェクトの特性に合わせて適切な構造を選択すること

らしいよ。 by ChatGPT、Claude,Gemini

ディレクトリ構成の参考例

project_root/
├── presentation/       # プレゼンテーション層
│   ├── controllers/    # ユーザーからの入力を受け取り、ビジネスロジック層に渡す
│   └── views/          # ユーザーに表示するUIやテンプレートを含む
├── business_logic/     # ビジネスロジック層
│   ├── services/       # アプリケーションの主要な機能やビジネスルールを実装
│   └── models/         # データの構造やビジネスオブジェクトを定義
└── data_access/        # データアクセス層
    ├── repositories/   # データアクセスを抽象化し、ビジネスロジック層に提供
    └── data_sources/   # 実際のデータベース接続やファイル操作などの実装

Webアプリ以外でも3層構造の概念は使える

Webアプリケーション以外のソフトウェアでも3層構造(または多層構造)の考え方は有効

この設計原則は、様々な種類のアプリケーションに適用できる柔軟性があります。以下に、いくつかの例を挙げて説明します。

デスクトップアプリケーション

デスクトップアプリケーションでも3層構造を適用できます。

src/
  ├── ui/                 # プレゼンテーション層
  │   ├── forms/
  │   ├── controls/
  │   └── viewmodels/
  ├── business/           # ビジネスロジック層
  │   ├── services/
  │   └── models/
  ├── data/               # データアクセス層
  │   ├── repositories/
  │   └── entities/
  ├── common/             # 共通のユーティリティや設定
  │   ├── utils/
  │   └── config/
  ├── test/               # テストディレクトリ
  │   ├── services/
  │   ├── repositories/
  │   └── utils/
  └── main.py             # アプリケーションのエントリーポイント

この構造では、UIフレームワーク(例:WPF、JavaFX)がプレゼンテーション層を、ビジネスロジックがアプリケーションの中核機能を、そしてデータアクセス層がローカルデータベースや外部APIとの通信を担当します。

それぞれどんなスクリプトを配置すべき?

1. ui/ (プレゼンテーション層)

ユーザーインターフェースに関連するすべてのコードを含みます。ユーザーとの直接的なやり取りを担当します。

forms/

役割: アプリケーションの各画面やウィンドウのレイアウトとデザインを定義します。
含めるスクリプト:
各フォームのデザインファイル(例:XML、XAML、JSONなど)。
フォームの初期化やイベントハンドラを定義するコード。
例:
MainForm.py:メインウィンドウのレイアウトと初期化。
SettingsForm.py:設定画面のUIとイベント処理。

controls/

役割: カスタムコントロールやウィジェットを作成し、再利用可能なUIコンポーネントを提供します。
含めるスクリプト:
カスタムボタン、テキストボックス、リストビューなどのコントロール定義。
コントロールの動作やスタイルを定義するコード。
例:
CustomButton.py:特定のスタイルや動作を持つボタン。
NumericTextBox.py:数値のみ入力可能なテキストボックス。

viewmodels/

役割: MVVM(Model-View-ViewModel)パターンを使用する場合のViewModelを格納し、UIとビジネスロジックの橋渡しを行います。
含めるスクリプト:
各フォームに対応するViewModelクラス。
データバインディングやコマンドの実装。
例:
MainViewModel.py:メイン画面のデータと操作を管理。
SettingsViewModel.py:設定画面のデータバインディングとロジック。

2. business/ (ビジネスロジック層)

アプリケーションの主要な機能やビジネスルールを実装します。

services/

役割: ビジネスロジックを実行するサービスクラスを提供し、特定の機能をカプセル化します。
含めるスクリプト:
アプリケーションの機能を実現するメソッド群を持つクラス。
複数のモデルやデータソースを組み合わせた処理。
例:
AuthenticationService.py:ユーザー認証やセッション管理。
OrderProcessingService.py:注文の作成、更新、キャンセル処理。

models/

役割: ビジネスドメインにおけるデータ構造とビジネスルールを定義します。
含めるスクリプト:
ビジネスオブジェクトのクラス定義。
データの検証やビジネスルールを含むメソッド。
例:
User.py:ユーザーの属性と操作。
Product.py:商品の情報と関連するビジネスロジック。

3. data/ (データアクセス層)

データベースやファイルシステムなどのデータソースとのやり取りを担当します。

repositories/

役割: データアクセスを抽象化し、ビジネスロジック層に統一的なデータ操作インターフェースを提供します。
含めるスクリプト:
CRUD(作成、読み取り、更新、削除)操作を行うクラス。
データベースクエリやファイル読み書きの実装。
例:
UserRepository.py:ユーザーデータの取得と保存。
ProductRepository.py:商品データの管理。

entities/

役割: データベーステーブルやデータストレージの構造を表現するエンティティクラスを定義します。
含めるスクリプト:
ORM(Object-Relational Mapping)で使用するエンティティクラス。
データベースフィールドとクラス属性のマッピング。
例:
UserEntity.py:ユーザーテーブルのエンティティ。
ProductEntity.py:商品テーブルのエンティティ。

4. common/

アプリケーション全体で共有される汎用的なコードや設定を含みます。

utils/

役割: 複数のモジュールやレイヤーで使用される共通のユーティリティ関数やヘルパークラスを提供します。
含めるスクリプト:
日付・時刻操作、文字列処理、ファイル操作などの汎用関数。
ロギングやエラーハンドリングのヘルパー。
例:
DateUtils.py:日付の変換やフォーマット。
FileUtils.py:ファイルの読み書きやパス操作。

config/

役割: アプリケーションの設定や環境変数、定数を管理します。
含めるスクリプト:
設定ファイルの読み込みとパース。
デフォルト設定や環境ごとの設定を定義。
例:
AppConfig.py:アプリケーション全体の設定管理。
DatabaseConfig.py:データベース接続設定。

個人的にrepositoriesがわかりづらかったのでもう少し調べてみた。

"repositories" の役割:

データアクセスの抽象化を行うデザインパターンです。つまり、データベースやファイルシステム、APIなどのデータソースに直接アクセスする方法を隠し、ビジネスロジック層がデータソースの具体的な仕組みを知らなくてもデータを操作できるようにするための仕組みです。

1. データアクセスの抽象化:

目的: ビジネスロジック層(business_logic/)がデータベースやファイルシステムなどの具体的なデータソースの詳細を知らずに、データを操作できるようにする。

具体的な役割:

データの取得、保存、更新、削除などの操作をメソッドとして提供。 ビジネスロジック層はこれらのメソッドを呼び出すだけでよい。

2. 変更への柔軟性:

データソースの変更: もしデータベースを変更したり、新しいデータソースを追加したりする場合でも、repositories/ の内部を変更するだけで済みます。ビジネスロジック層には影響がありません。

3. テストの容易化:

モックの使用: テスト時にリポジトリをモックに差し替えることで、データベースに依存しないテストが可能になります。

具体的な例:
ユーザー情報を扱う場合:

repositories/UserRepository

メソッド例:
getUserById(id)
getAllUsers()
createUser(userData)
updateUser(id, userData)
deleteUser(id)
data_sources/DatabaseConnection

データベースへの接続やクエリの実行を担当。
ビジネスロジック層での使用:

UserService(business_logic/services/内)が UserRepository を利用してユーザー情報を操作。

例えばこんなスクリプトを作る


# data/repositories/user_repository.py

class UserRepository:
    def __init__(self, database):
        # データベース接続(依存性注入)
        self.database = database

    def get_user_by_id(self, user_id):
        # データベースからユーザー情報を取得
        user_data = self.database.execute_query(f"SELECT * FROM users WHERE id = {user_id}")
        return user_data

    def save_user(self, user_data):
        # データベースに新しいユーザー情報を保存
        self.database.execute_query(f"INSERT INTO users (name, email) VALUES ({user_data['name']}, {user_data['email']})")

    def update_user(self, user_id, user_data):
        # ユーザー情報を更新
        self.database.execute_query(f"UPDATE users SET name = {user_data['name']}, email = {user_data['email']} WHERE id = {user_id}")

    def delete_user(self, user_id):
        # ユーザーを削除
        self.database.execute_query(f"DELETE FROM users WHERE id = {user_id}")

ビジネスロジック層での使用例

# business/services/user_service.py

class UserService:
    def __init__(self, user_repository):
        # UserRepositoryを依存性注入
        self.user_repository = user_repository

    def get_user_profile(self, user_id):
        # ユーザー情報を取得し、ビジネスロジックを適用
        user_data = self.user_repository.get_user_by_id(user_id)
        # 取得したユーザー情報に何らかのビジネスロジックを適用
        return user_data

    def create_user(self, user_data):
        # ユーザーを作成するビジネスロジックを実行
        self.user_repository.save_user(user_data)

データアクセス層の実装例

# data/entities/user_entity.py

class UserEntity:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    def to_dict(self):
        # エンティティを辞書に変換してリポジトリに渡す
        return {'id': self.id, 'name': self.name, 'email': self.email}

使い方の流れ
ビジネスロジック層であるUserServiceは、UserRepositoryを利用してデータベースからユーザー情報を取得・保存します。
UserRepositoryは、データベースとのやり取りを管理し、ビジネスロジック層に必要なデータを提供します。
実際のデータ操作はUserRepositoryが行い、ビジネスロジック層はその詳細を気にしなくても済むように抽象化されています。

リポジトリの主な役割は、データソース(データベース、API、ファイルなど)とのやり取りを抽象化し、ビジネスロジック層がデータアクセスの詳細を知らずにデータ操作を行えるようにすることです。
リポジトリパターンを使うことで、コードの保守性が向上し、データソースが変更されても、ビジネスロジック層に影響を与えずに修正できます。また、テストのしやすさも利点です。
このように、リポジトリはビジネスロジックとデータアクセスを分離し、アプリケーションの柔軟性や拡張性を高める役割を果たします。

モバイルアプリケーション

モバイルアプリでも同様の構造を適用できます。

src/
  ├── ui/                 # プレゼンテーション層
  │   ├── screens/
  │   ├── components/
  │   └── viewmodels/
  ├── domain/             # ビジネスロジック層
  │   ├── usecases/
  │   └── models/
  ├── data/               # データアクセス層
  │   ├── repositories/
  │   ├── datasources/
  │   └── entities/
  └── core/
  │    ├── utils/
  │    └── config/
  └── main.py             # アプリケーションのエントリーポイント

この構造では、UIフレームワーク(例:SwiftUI、Jetpack Compose)がプレゼンテーション層を、ドメインロジックがビジネスロジック層を、そしてデータ層がローカルストレージやAPIとの通信を担当します。

組み込みシステム

組み込みシステムでも、リソースの制約はありますが、類似の構造を適用できます:

プレゼンテーション層:デバイスの表示やユーザー入力の処理
ビジネスロジック層:システムの主要な機能や制御ロジック
データアクセス層:センサーデータの読み取りや永続化ストレージとのやりとり

コマンドラインアプリケーション

CLIアプリでも、シンプルながら3層構造の考え方を適用できます:

プレゼンテーション層:コマンドライン引数の解析、出力の整形
ビジネスロジック層:主要な処理ロジック
データアクセス層:ファイルシステムやデータベースとのやりとり

これらの例から分かるように、3層構造の考え方は様々な種類のアプリケーションに適用可能です。ただし、アプリケーションの性質や規模によっては、完全な3層構造を適用するのではなく、その考え方を参考にしつつ、より簡素化された構造を採用することもあります。
重要なのは、アプリケーションの要件、パフォーマンスの制約、開発チームの経験などを考慮しながら、適切な構造を選択することです。3層構造の基本的な考え方(関心の分離、モジュール性の向上、保守性の改善)を理解し、それをプロジェクトに合わせて柔軟に適用することが大切です。

ありがとうChatGPT,Claude,Geminiってことで設計について勉強してみました。

この記事が気に入ったらサポートをしてみませんか?