0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クリーンアーキテクチャとMVCフレームワークの対応付けをやってみる

Last updated at Posted at 2025-07-12

はじめに:本記事の狙い

クリーンアーキテクチャ(以下、CA)での設計しか知らない私が、Ruby on RailsやLaravelに代表されるMVCフレームワークを用いた開発へスムーズに適応できるよう、両者の概念を(無理やり)対応付け、思考法を整理することが本記事の狙いである。

クリーンアーキテクチャとMVCの概念対応

まず、CAの各層・概念が、一般的なMVCフレームワークにおいてどのコンポーネントに対応するのかを以下に示す。

クリーンアーキテクチャの概念 MVCフレームワークでの主な実装先
エンティティ (Entity) Model (ActiveRecord モデル)
値オブジェクト (Value Object) PORO/POPO をModel内で利用
リポジトリ (Repository) Model (ActiveRecord が責務を兼任)
ユースケース / ドメインサービス Service Object (app/servicesなど)
コントローラー (Controller) Controller (app/controllers)
プレゼンター (Presenter) Presenter/Decoratorパターン (app/presentersなど)
ルーター (Router) ルーティングファイル (config/routes.rbなど)
データベース (Database) ORM (ActiveRecord とデータベース設定)

以下はクリーンアーキテクチャの実装例
image.png

以下では、「ユーザー登録機能」をRuby on Railsで実装する例を交えながら、各概念の対応関係を詳述する。

1. ドメイン層 (Domain Layer)

ドメイン層は、ビジネスの核となるルールやロジックを担う。MVCフレームワークでは、これらの責務は主にModelや、必要に応じて作成されるServiceクラスに配置される。

エンティティ (Entity)

一意なIDを持ち、状態が変化するビジネスオブジェクト(例: User, Product)である。

  • MVCでの対応:
    RailsのActiveRecordやLaravelのEloquentといったO/Rマッパーのモデルクラスが、エンティティの役割を兼任するのが最も一般的である。app/modelsに配置されるこれらのクラスが、ビジネスルール(validatesなど)と永続化のロジックを両方持つ。

実装例: ActiveRecordによるエンティティ

# app/models/user.rb

# ApplicationRecordはActiveRecord::Baseを継承している
class User < ApplicationRecord
  # エンティティとしてのビジネスルール(状態を検証する)
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }

  # 永続化に関するロジックもここに含む
  has_secure_password
end

このUserクラスは、ActiveRecordの機能を使ってデータベースとの連携を担いつつ、ビジネスルールを定義するエンティティとしての役割も兼ね備えている。

値オブジェクト (Value Object)

単なる値としての性質を持つオブジェクト(例: Money, ZipCode)である。

  • MVCでの対応: Active Recordcomposed_of (Rails) や、カスタムキャスト (Laravel) を利用してModelの属性として実装可能だ。または、単純なPORO (Plain Old Ruby Object) / POPO (Plain Old PHP Object) として定義し、Model内で利用する方法も一般的である。

実装例: ZipCode値オブジェクト

# app/models/value_objects/zip_code.rb
# (カスタムディレクトリを作成して管理する)

class ZipCode
  attr_reader :value

  def initialize(value)
    raise ArgumentError, 'Invalid zip code format' unless value.match?(/\A\d{3}-\d{4}\z/)
    @value = value
  end

  def to_s
    @value
  end
end

この値オブジェクトをUserモデル内で利用することで、単なる文字列ではない、意味と制約を持った属性として扱うことができる。

ドメインサービス (Domain Service)

特定のエンティティに属さない、複数のエンティティをまたぐドメイン固有のロジックである。

  • MVCでの対応: サービスオブジェクト (Service Object)サービスクラス (Service Class) として実装する。後述のユースケースと合わせて実装されることも多い。

2. ユースケース層 (Use Case Layer)

アプリケーション固有のビジネスルールや、具体的な操作手順を定義する。

ユースケース (Use Case)

システムの操作(例: 「ユーザーを作成する」)を表現する。

  • MVCでの対応: ドメインサービスと同様に、サービスオブジェクトやサービスクラスで実装するのが一般的だ。Controllerからこのサービスクラスを呼び出すことで、Controllerを薄く保つ(Fat Controllerの回避)ことができる。app/servicesのようなディレクトリを作成し、UserRegistrationServiceのように具体的な処理をクラスとして定義する。

実装例: ユーザー登録ユースケース

# app/services/user_registration_service.rb

class UserRegistrationService
  Result = Struct.new(:success?, :user, :errors, keyword_init: true)

  def initialize(params)
    @params = params
  end

  def execute
    user = User.new(@params)

    if user.save
      # 成功した場合の付随処理(ドメインサービス的な振る舞い)
      send_welcome_email(user)
      Result.new(success?: true, user: user)
    else
      # 失敗した場合
      Result.new(success?: false, errors: user.errors.full_messages)
    end
  end

  private

  def send_welcome_email(user)
    # Mailerクラスなどを呼び出す
    puts "Welcome email sent to #{user.email}!"
  end
end

3. アダプタ層 (Adapter Layer)

ドメイン層やユースケース層と、外部の技術(フレームワーク、UI)との間でデータ変換を行う。

コントローラー (Controller)

外部からのリクエストを受け取り、ユースケースを呼び出し、レスポンスを返す。

  • MVCでの対応: MVCフレームワークのControllerがほぼ同じ役割を担う。ただし、CAほど責務が厳密ではなく、リクエストの受付、ユースケース(Service)の呼び出し、レスポンス(ViewのレンダリングやJSON返却)までを担当する。

実装例: UsersController

# app/controllers/users_controller.rb

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    # 1. ユースケース(Service)を呼び出す
    result = UserRegistrationService.new(user_params).execute

    # 2. 結果に応じてレスポンスを決定する
    if result.success?
      redirect_to user_path(result.user), notice: 'User was successfully created.'
    else
      @user = User.new(user_params)
      flash.now[:alert] = result.errors.join(', ')
      render :new, status: :unprocessable_entity
    end
  end

  # ... 省略 ...
  private
  def user_params
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
  end
end

Controllerはビジネスロジックの詳細を知らず、UserRegistrationServiceに処理を委譲している点が重要だ。

プレゼンター (Presenter)

ユースケースからの出力を、Viewに適した形に整形する。

  • MVCでの対応: 必須ではないが、DecoratorパターンやPresenterパターンを用いて実装可能である。Viewのロジックが複雑化した場合に導入する。単純な場合は、Viewファイル内やヘルパーメソッドで整形を済ませることが多い。

実装例: UserPresenter

# app/presenters/user_presenter.rb

class UserPresenter < SimpleDelegator
  # SimpleDelegatorを継承するとメソッド委譲を自動化できる

  # View向けの整形ロジック
  def registration_date
    created_at.strftime('%Y年%m月%d日')
  end

  def name_with_honorific
    "#{name} 様"
  end
end

リポジトリ (Repository)

エンティティの永続化を抽象化するインターフェースである。

  • MVCでの対応: Active Recordモデルがこの役割を暗黙的に兼任しているUser.find(1)user.saveといったメソッドが、リポジトリパターンの責務を果たしていると見なせる。そのため、リポジトリ層を明示的に作成することは少ない。

4. インフラ層 (Infrastructure Layer)

フレームワークやデータベースなど、具体的な技術要素である。

ルーター (Router)

リクエストを適切なコントローラーに振り分ける。

  • MVCでの対応: フレームワークが提供するルーティング機能そのものが対応する。(例: Railsのconfig/routes.rb

実装例: ルーティング定義

# config/routes.rb

Rails.application.routes.draw do
  resources :users, only: [:new, :create, :show]
end

データベース (Database)

データの永続化を行う具体的なストレージ。

  • MVCでの対応: フレームワークのデータベース設定とO/Rマッパー(ORM)が対応する。Active RecordEloquentを通じて、開発者はSQLを意識せずにデータを操作できる。

結論:両者の思想の違いと橋渡し

CAの各概念は、MVCフレームワークの思想に合わせて、既存のコンポーネントに役割を統合したり、新たなディレクトリ(例: app/services)を作成したりすることで対応可能である。

  • ドメイン層・ユースケース層のロジックModelService層 に集約。
  • リポジトリの責務Model (Active Record) が兼任。
  • プレゼンターの責務View/Helper または Decorator/Presenterパターンで対応。

image.png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?