0
0

システムアーキテクチャ7種の比較

Posted at

1. はじめに

このドキュメントでは、Slack botアプリケーションを題材に、さまざまなシステムアーキテクチャを比較します。比較対象のアーキテクチャは以下の通りです。

  • MVC(Model-View-Controller)
  • MVP (Model-View-Presenter)
  • Layered Architecture
  • Hexagonal Architecture (Ports & Adapters)
  • Clean Architecture
  • Onion Architecture
  • アーキテクチャなし (必要最小限のモジュールのみを実装)

各アーキテクチャの特徴を説明し、Pythonを用いてSlack botアプリケーションを実装する際の構成例を示します。

2. Slack botアプリケーションの概要

今回比較に用いるSlack botアプリケーションは、以下の機能を持つシンプルなものとします。

  • ユーザーからのメッセージを受信し、応答を返す
  • 特定のコマンドに反応し、対応する処理を実行する
  • Google Cloudのサービス(例:Cloud Storage)を利用する

これらの機能を実現するために必要な要素は以下の通りです。

  • Slack APIとの通信
  • メッセージの解析と応答の生成
  • コマンドの解析と対応する処理の実行
  • Google Cloudサービスとの連携

3. アーキテクチャの比較

3.1 MVC(Model-View-Controller)

MVCアーキテクチャは、以下の3つの要素に分割してアプリケーションを構築します。

  • Model:アプリケーションのデータと業務ロジックを担当
  • View:ユーザーインターフェースを担当
  • Controller:ModelとViewの間の調整役となり、ユーザーの入力を受け取ってModelに伝え、Modelの変更をViewに反映

Slack botアプリケーションをMVCアーキテクチャで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── controllers
│   └── message_controller.py
├── models
│   └── message_model.py
└── views
    └── slack_view.py

コード概要

  • message_controller.py

    from models.message_model import MessageModel
    from views.slack_view import SlackView
    
    class MessageController:
        def __init__(self):
            self.model = MessageModel()
            self.view = SlackView()
    
        def handle_message(self, message: str):
            self.model.analyze_message(message)
            response = self.model.generate_response(message)
            self.view.send_response(response)
    
  • message_model.py

    class MessageModel:
        def __init__(self):
            self.messages = []
    
        def analyze_message(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • slack_view.py

    class SlackView:
        def render_message(self, message: str):
            # メッセージを表示する処理
            print(message)
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    

3.2 MVP (Model-View-Presenter)

MVPアーキテクチャは、以下の3つの要素に分割してアプリケーションを構築します。

  • Model:アプリケーションのデータと業務ロジックを担当
  • View:ユーザーインターフェースを担当し、Presenterからの指示に従ってUIを更新
  • Presenter:ViewとModelの間の調整役となり、Viewからの入力を受け取ってModelに伝え、Modelの変更をViewに反映

Slack botアプリケーションをMVPアーキテクチャで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── models
│   └── message_model.py
├── presenters
│   └── message_presenter.py
└── views
    └── slack_view.py

コード概要

  • message_presenter.py

    from models.message_model import MessageModel
    from views.slack_view import SlackView
    
    class MessagePresenter:
        def __init__(self):
            self.model = MessageModel()
            self.view = SlackView(self)
    
        def handle_message(self, message: str):
            self.model.analyze_message(message)
            response = self.model.generate_response(message)
            self.update_view(response)
    
        def update_view(self, response: str):
            self.view.send_response(response)
    
  • message_model.py

    class MessageModel:
        def __init__(self):
            self.messages = []
    
        def analyze_message(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • slack_view.py

    class SlackView:
        def __init__(self, presenter):
            self.presenter = presenter
    
        def render_message(self, message: str):
            # メッセージを表示する処理
            print(message)
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
        def on_message_received(self, message: str):
            self.presenter.handle_message(message)
    

3.3 Layered Architecture

Layered Architectureは、アプリケーションを複数の層に分割して構築するアーキテクチャです。一般的には、以下のような層構成になります。

  • Presentation Layer:ユーザーインターフェースを担当
  • Application Layer:アプリケーションの業務ロジックを担当
  • Domain Layer:アプリケーションのコアとなるビジネスロジックを担当
  • Infrastructure Layer:外部サービスとの連携や、データの永続化を担当

Slack botアプリケーションをLayered Architectureで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── application
│   └── message_handler.py
├── domain
│   └── message.py
├── infrastructure
│   ├── google_cloud_service.py
│   └── message_repository.py
└── presentation
    └── slack_ui.py

コード概要

  • message_handler.py

    from domain.message import Message
    from infrastructure.message_repository import MessageRepository
    from presentation.slack_ui import SlackUI
    
    class MessageHandler:
        def __init__(self):
            self.message = Message()
            self.repository = MessageRepository()
            self.ui = SlackUI(self)
    
        def handle_message(self, message: str):
            self.message.analyze(message)
            response = self.message.generate_response(message)
            self.repository.store_message(message)
            self.ui.send_response(response)
    
  • message.py

    class Message:
        def __init__(self):
            self.messages = []
    
        def analyze(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • message_repository.py

    class MessageRepository:
        def store_message(self, message: str):
            # メッセージをデータストアに保存する処理
            pass
    
        def load_messages(self) -> List[str]:
            # データストアからメッセージを読み込む処理
            return []
    
  • google_cloud_service.py

    class GoogleCloudService:
        def connect_to_google_cloud(self):
            # Google Cloudサービスに接続する処理
            pass
    
  • slack_ui.py

    class SlackUI:
        def __init__(self, handler):
            self.handler = handler
    
        def render_message(self, message: str):
            # メッセージを表示する処理
            print(message)
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
        def on_message_received(self, message: str):
            self.handler.handle_message(message)
    

3.4 Hexagonal Architecture (Ports & Adapters)

Hexagonal Architecture (Ports & Adapters) は、アプリケーションのコアロジックを外部の依存関係から分離することを目的としたアーキテクチャです。アプリケーションを以下の3つの要素に分割します。

  • Domain:アプリケーションのコアロジックを含む
  • Ports:ドメインと外部の間のインターフェースを定義
  • Adapters:外部サービスやユーザーインターフェースとの連携を担当

Slack botアプリケーションをHexagonal Architectureで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── adapters
│   ├── google_cloud_adapter.py
│   └── slack_adapter.py
├── domain
│   └── message.py
└── ports
    └── message_port.py

コード概要

  • message.py

    from ports.message_port import MessagePort
    
    class Message:
        def __init__(self, port: MessagePort):
            self.messages = []
            self.port = port
    
        def analyze(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
        def send_response(self, response: str):
            self.port.send_response(response)
    
  • message_port.py

    from abc import ABC, abstractmethod
    
    class MessagePort(ABC):
        @abstractmethod
        def send_response(self, response: str):
            pass
    
        @abstractmethod
        def on_message_received(self, message: str):
            pass
    
  • slack_adapter.py

    from ports.message_port import MessagePort
    
    class SlackAdapter(MessagePort):
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
        def on_message_received(self, message: str):
            # メッセージを受信した際の処理
            pass
    
        def connect(self):
            # Slack APIに接続する処理
            pass
    
        def disconnect(self):
            # Slack APIから切断する処理
            pass
    
  • google_cloud_adapter.py

    class GoogleCloudAdapter:
        def store_data(self, data: str):
            # Google Cloudにデータを保存する処理
            pass
    
        def load_data(self) -> str:
            # Google Cloudからデータを読み込む処理
            return ""
    
        def connect(self):
            # Google Cloudサービスに接続する処理
            pass
    
        def disconnect(self):
            # Google Cloudサービスから切断する処理
            pass
    

3.5 Clean Architecture

Clean Architectureは、アプリケーションを複数の同心円状の層に分割し、各層の責務を明確に分離することを目的としたアーキテクチャです。内側の層は外側の層に依存せず、外側の層は内側の層に依存します。

  • Entities:アプリケーションのビジネスルールを表現
  • Use Cases:アプリケーションの機能を実装
  • Interface Adapters:外部サービスやユーザーインターフェースとの連携を担当
  • Frameworks and Drivers:最も外側の層で、具体的な実装や外部ライブラリを含む

Slack botアプリケーションをClean Architectureで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── entities
│   └── message.py
├── frameworks_and_drivers
│   ├── google_cloud_service.py
│   └── slack_api.py
├── interface_adapters
│   ├── message_gateway.py
│   └── slack_ui.py
└── use_cases
    └── message_usecase.py

コード概要

  • message.py

    class Message:
        def __init__(self):
            self.messages = []
    
        def analyze(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
  • message_usecase.py

    from entities.message import Message
    
    class MessageUseCase:
        def __init__(self, gateway):
            self.message = Message()
            self.gateway = gateway
    
        def handle_message(self, message: str):
            self.message.analyze(message)
            response = self.message.generate_response(message)
            self.gateway.send_response(response)
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • message_gateway.py

    class MessageGateway:
        def __init__(self, slack_ui, google_cloud_service):
            self.slack_ui = slack_ui
            self.google_cloud_service = google_cloud_service
    
        def send_response(self, response: str):
            self.slack_ui.send_response(response)
    
        def on_message_received(self, message: str):
            # メッセージを受信した際の処理
            pass
    
        def store_data(self, data: str):
            self.google_cloud_service.store_data(data)
    
        def load_data(self) -> str:
            return self.google_cloud_service.load_data()
    
  • slack_ui.py

    class SlackUI:
        def __init__(self, slack_api):
            self.slack_api = slack_api
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
        def on_message_received(self, message: str):
            # メッセージを受信した際の処理
            pass
    
  • google_cloud_service.py

    class GoogleCloudService:
        def store_data(self, data: str):
            # Google Cloudにデータを保存する処理
            pass
    
        def load_data(self) -> str:
            # Google Cloudからデータを読み込む処理
            return ""
    
  • slack_api.py

    class SlackAPI:
        def connect(self):
            # Slack APIに接続する処理
            pass
    
        def disconnect(self):
            # Slack APIから切断する処理
            pass
    

3.6 Onion Architecture

Onion Architectureは、Clean Architectureと同様に、アプリケーションを複数の同心円状の層に分割するアーキテクチャです。内側の層は外側の層に依存せず、外側の層は内側の層に依存します。

  • Domain Model:アプリケーションのコアとなるビジネスロジック
  • Domain Services:Domain Modelを補完する域サービス
  • Application Services:アプリケーションの機能を実装
  • Infrastructure:外部サービスやユーザーインターフェースとの連携を担当

Slack botアプリケーションをOnion Architectureで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── application_services
│   └── message_service.py
├── domain_model
│   └── message.py
├── domain_services
│   └── command_service.py
└── infrastructure
    ├── google_cloud_repository.py
    ├── slack_api.py
    └── slack_ui.py

コード概要

  • message.py

    class Message:
        def __init__(self):
            self.messages = []
    
        def analyze(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
  • command_service.py

    class CommandService:
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • message_service.py

    from domain_model.message import Message
    from domain_services.command_service import CommandService
    
    class MessageService:
        def __init__(self, infrastructure):
            self.message = Message()
            self.command_service = CommandService()
            self.infrastructure = infrastructure
    
        def handle_message(self, message: str):
            self.message.analyze(message)
            response = self.message.generate_response(message)
            self.infrastructure.send_response(response)
            self.command_service.execute_command(message)
    
  • slack_ui.py

    class SlackUI:
        def __init__(self, slack_api):
            self.slack_api = slack_api
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
        def on_message_received(self, message: str):
            # メッセージを受信した際の処理
            pass
    
  • slack_api.py

    class SlackAPI:
        def connect(self):
            # Slack APIに接続する処理
            pass
    
        def disconnect(self):
            # Slack APIから切断する処理
            pass
    
  • google_cloud_repository.py

    class GoogleCloudRepository:
        def store_data(self, data: str):
            # Google Cloudにデータを保存する処理
            pass
    
        def load_data(self) -> str:
            # Google Cloudからデータを読み込む処理
            return ""
    
        def connect(self):
            # Google Cloudサービスに接続する処理
            pass
    
        def disconnect(self):
            # Google Cloudサービスから切断する処理
            pass
    

3.7 アーキテクチャなし (必要最小限のモジュールのみを実装)

アーキテクチャを採用せず、必要最小限のモジュールのみを実装するパターンもあります。このアプローチでは、機能の実装に必要な要素だけを含めます。

Slack botアプリケーションを必要最小限のモジュールで実装する場合、以下のような構成になります。

ディレクトリ構造

.
├── google_cloud_service.py
├── message_handler.py
└── slack_bot.py

コード概要

  • slack_bot.py

    from message_handler import MessageHandler
    from google_cloud_service import GoogleCloudService
    
    class SlackBot:
        def __init__(self):
            self.message_handler = MessageHandler()
            self.google_cloud_service = GoogleCloudService()
    
        def start(self):
            # Slack botを起動する処理
            pass
    
        def on_message_received(self, message: str):
            response = self.message_handler.generate_response(message)
            self.send_response(response)
            self.message_handler.execute_command(message)
    
        def send_response(self, response: str):
            # Slack APIを使って応答を送信する処理
            pass
    
  • message_handler.py

    class MessageHandler:
        def analyze_message(self, message: str):
            # メッセージを解析する処理
            pass
    
        def generate_response(self, message: str) -> str:
            # メッセージに対する応答を生成する処理
            return "This is a response."
    
        def execute_command(self, command: str):
            # コマンドに対応する処理を実行
            pass
    
  • google_cloud_service.py

    class GoogleCloudService:
        def store_data(self, data: str):
            # Google Cloudにデータを保存する処理
            pass
    
        def load_data(self) -> str:
            # Google Cloudからデータを読み込む処理
            return ""
    

4. 各アーキテクチャの特徴と比較

アーキテクチャ 特徴 メリット デメリット
MVC Model、View、Controllerに分割 各コンポーネントの役割が明確、変更の影響範囲を限定できる Controllerが肥大化しやすい、Modelの変更がViewに影響を与えやすい
MVP Model、View、Presenterに分割 Viewとモデルを分離できる、Presenterのテストが容易 Presenterが肥大化しやすい、Viewの変更がPresenterに影響を与えやすい
Layered Architecture 複数の層に分割 各層の責務が明確、下位の層の変更が上位の層に影響を与えにくい 層間の依存関係が複雑になりやすい、パフォーマンスが低下しやすい
Hexagonal Architecture Domain、Ports、Adaptersに分割 ドメインロジックを外部の依存関係から分離できる、テストが容易 実装が複雑になりやすい、アダプターの数が増えると管理が難しくなる
Clean Architecture Entities、Use Cases、Interface Adapters、Frameworks and Driversの同心円状の層に分割 ドメインロジックを外部の依存関係から分離できる、テストが容易 実装が複雑になりやすい、層間の依存関係の管理が難しい
Onion Architecture Domain Model、Domain Services、Application Services、Infrastructureの同心円状の層に分割 ドメインロジックを外部の依存関係から分離できる、テストが容易 実装が複雑になりやすい、層間の依存関係の管理が難しい

アーキテクチャを採用しない場合は、シンプルで理解しやすいコードになりますが、アプリケーションが大規模になると、コードの複雑性が増大し、メンテナンス性が低下する可能性があります。

5. まとめ

各アーキテクチャには長所と短所があり、アプリケーションの要件や規模に応じて適切なアーキテクチャを選択することが重要です。

小規模なアプリケーションであれば、MVCやMVPなどのシンプルなアーキテクチャで十分な場合もあります。一方、大規模なアプリケーションや、変更に強い設計が求められる場合は、Clean ArchitectureやOnion Architectureなどの、ドメインロジックを中心に設計されたアーキテクチャが適しているでしょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0