The Codest
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 産業
    • フィンテック&バンキング
    • E-commerce
    • アドテック
    • ヘルステック
    • 製造業
    • 物流
    • 自動車
    • アイオーティー
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
戻る矢印 戻る
2019-03-08
ソフトウェア開発

クエリオブジェクトによるコードの最適化

The Codest

トマシュ・シュカラデク

開発アーキテクト

仕事で、オーバーロードされたモデルやコントローラ内の膨大な数の呼び出しに何度も遭遇している可能性はかなり高いでしょう。この記事では、Rails環境での知識に基づいて、この問題の簡単な解決策を提案します。

railsアプリケーションの非常に重要な側面は、冗長な依存関係の数を最小限に抑えることです。そのため、Rails環境全体が最近、サービスオブジェクトのアプローチとPORO(Pure Old ルビー Object)メソッドを使用します。このようなソリューションの使用方法については、以下を参照してください。 これ.この記事では、ステップ・バイ・ステップでコンセプトを解き、問題に適応させていく。

問題点

仮想的なアプリケーションでは、複雑なトランザクションシステムを扱っている。各トランザクションを表す我々のモデルは、データを取得するのに役立つスコープのセットを持っています。これは1つの場所で見つけることができるため、素晴らしい仕事の円滑化です。しかし、これは長くは続かない。アプリケーションの開発とともに プロジェクト はますます複雑になっている。スコープはもはや単純な "WHERE "参照ではなくなり、データが不足し、リレーションシップをロードし始める。しばらくすると、それはミラーの複雑なシステムを思い起こさせる。さらに悪いことに、複数行のラムダをどうやればいいのかわからない!

以下に、すでに拡張されたアプリケーション・モデルを示す。決済システムのトランザクションが格納されています。下の例でわかるように

クラス Transaction  { where(visible: true) }.

  scope(:active, lambda do
    結合(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end)
終了

モデルもそうですが、プロジェクトの規模が大きくなるにつれて、コントローラーも大きくなっていきます。下の例を見てみよう:

class TransactionsController < ApplicationController
  def index
    @transactions = Transaction.for_publishers
                                   .active
                                   .visible
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    トランザクション = apply_filters(@transactions)
  終了
終了

ここでは、何行もの連鎖メソッドと、多くの場所で実行したくない追加の結合が並んでいるのがわかる。添付されたデータは後でapply_filtersメソッドで使われ、GETパラメータに基づいて適切なデータフィルタリングを追加する。もちろん、これらの参照のいくつかをスコープに移すことはできるが、実際に解決しようとしているのはこの問題ではないだろうか?

ソリューション

私たちが抱えている問題はすでに分かっているのだから、これを解決しなければならない。冒頭のリファレンスに基づき、ここではPOROアプローチを使用する。この正確なケースでは、このアプローチはクエリーオブジェクトと呼ばれ、サービスオブジェクトのコンセプトを発展させたものである。

プロジェクトのappsディレクトリにservicesという新しいディレクトリを作りましょう。そこに トランザクションクエリ.

クラス TransactionsQuery
終了

次のステップとして、オブジェクトのデフォルト・コール・パスを作成するイニシャライザーを作成する必要があります。

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了
終わり

このおかげで、リレーションシップをアクティブレコードから施設に転送することができます。これで、提示されたコントローラでのみ必要とされる、すべてのスコープをクラスに転送することができます。

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  プライベート

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  終了

  def visible(scope)
    scope.where(visible: true)
  終了

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  終了
終了

最も重要な部分、つまりデータを1つの文字列に集め、インターフェイスをパブリックにすることがまだできていない。すべてをくっつけるメソッドの名前は「コール」とする。

本当に重要なのは、@scopeインスタンス変数を使うことだ。

クラス TransactionsQuery
  ...
  def call
    可視(@スコープ)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  終了

  非公開
  ...
終了

クラス全体は次のように発表している:

クラス TransactionsQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  def call
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  終了

  プライベート

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  終了

  def visible(scope)
    scope.where(visible: true)
  終了

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  終了
終了

クリーンアップ後、モデルの見た目は確実に軽くなった。ここでは、データの検証と他のモデルとの関係だけに集中する。

クラス Transaction < ActiveRecord::Base
  所属 :口座
  has_one :引き出し項目
終了

コントローラはすでに私たちの解決策を実装しています。追加のクエリはすべて別のクラスに移動しました。しかし、モデルになかった呼び出しは未解決のままです。いくつかの変更を経て、インデックスアクションは次のようになりました:

class TransactionsController < ApplicationController
  def index
    @transactions = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id")
                                     .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    トランザクション = apply_filters(@transactions)
  終了
終了

ソリューション

グッドプラクティスと慣例を実施する場合、与えられた問題の類似した発生をすべて置き換えることが良いアイデアかもしれません。したがって、SQLクエリをインデックス・アクションから別のクエリ・オブジェクトに移動します。これを TransactionsFilterableQuery クラスを用意する。私たちが準備する授業スタイルは、以下の授業で紹介するスタイルに似ています。 トランザクションクエリ.その一環として コード と呼ばれる複数行の文字列を使用して、より直感的に大規模な SQL クエリを記録できるようになります。 ヘレドック 解決策は以下にある:

クラス TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    スコープ = スコープ
  終了

  def call
    withdrawal(@scope).then(&method(:withdrawal_items))
  終了

  プライベート

  def withdrawal(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
      (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')
    SQL
  終了

  def withdrawal_items(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id
    SQL
  終了
終了

コントローラーに変更があった場合は、クエリー・オブジェクトを追加することで行数を減らす。重要なのは、ページ分割を担当する部分以外はすべて切り離すことだ。

class TransactionsController < ApplicationController
  def index
    TransactionsFilterableQuery.new(scope).call.then do |scope|.
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    トランザクション = apply_filters(@transactions)
  end
エンド

概要

クエリオブジェクトはSQLクエリを書くアプローチを大きく変える。ActiveRecordでは、すべてのビジネス・ロジックとデータベース・ロジックが一箇所にまとまっているため、モデル内にすべてのビジネス・ロジックとデータベース・ロジックを配置することが非常に簡単です。これは、小規模なアプリケーションでは非常に有効です。プロジェクトの複雑さが増すにつれて、ロジックを他の場所に設定していきます。同じクエリオブジェクトで、メンバーのクエリを特定の問題にグループ化することができます。

Thanks to this, we have an easy possibility of the code’s later inheritance and because of duck typing, you can also use these solutions in other models. The disadvantage of this solution is a larger amount of code and fragmentation of responsibility. However, whether we want to take up such a challenge or not, depends on us and how badly we are disturbed by fat models.

関連記事

上昇する矢印とコスト効率や節約を象徴する金貨が描かれた減少する棒グラフの抽象的なイラスト。左上にはThe Codestのロゴと、ライトグレーの背景に "In Code We Trust "のスローガン。
ソフトウェア開発

製品の品質を落とさずに開発チームを拡大する方法

開発チームの規模を拡大中ですか?製品の品質を犠牲にすることなく成長する方法を学びましょう。このガイドでは、スケールする時期、チーム構成、採用、リーダーシップ、ツールなどの兆候に加え、The Codestがどのように...

ザ・コデスト
ソフトウェア開発

将来を見据えたウェブ・アプリケーションの構築:The Codestのエキスパート・チームによる洞察

The Codestが、最先端技術を駆使してスケーラブルでインタラクティブなウェブアプリケーションを作成し、あらゆるプラットフォームでシームレスなユーザー体験を提供することにどのように秀でているかをご覧ください。The Codestの専門知識がどのようにデジタルトランスフォーメーションとビジネス...

ザ・コデスト
ソフトウェア開発

ラトビアを拠点とするソフトウェア開発企業トップ10社

ラトビアのトップソフトウェア開発企業とその革新的なソリューションについて、最新記事でご紹介します。ラトビアの技術リーダーたちがあなたのビジネスをどのように向上させるかをご覧ください。

thecodest
エンタープライズ&スケールアップ・ソリューション

Javaソフトウェア開発の要点:アウトソーシングを成功させるためのガイド

outsourcingのJavaソフトウェア開発を成功させるために不可欠なこのガイドを読んで、The Codestで効率性を高め、専門知識にアクセスし、プロジェクトを成功に導きましょう。

thecodest
ソフトウェア開発

ポーランドにおけるアウトソーシングの究極ガイド

ポーランドのoutsourcingの急増は、経済、教育、技術の進歩がITの成長とビジネス・フレンドリーな環境を促進していることによる。

ザ・コデスト

ナレッジベースを購読して、IT部門の専門知識を常に最新の状態に保ちましょう。

    会社概要

    The Codest - ポーランドに技術拠点を持つ国際的なソフトウェア開発会社。

    イギリス - 本社

    • オフィス 303B, 182-184 High Street North E6 2JA
      イギリス、ロンドン

    ポーランド - ローカル・テック・ハブ

    • ファブリチュナ・オフィスパーク、アレハ
      ポコジュ18、31-564クラクフ
    • ブレイン・エンバシー, コンストルクトースカ
      11, 02-673 Warsaw, Poland

      The Codest

    • ホーム
    • 会社概要
    • サービス
    • Case Studies
    • ノウハウ
    • 採用情報
    • 辞書

      サービス

    • アドバイザリー
    • ソフトウェア開発
    • バックエンド開発
    • フロントエンド開発
    • Staff Augmentation
    • バックエンド開発者
    • クラウドエンジニア
    • データエンジニア
    • その他
    • QAエンジニア

      リソース

    • 外部ソフトウェア開発パートナーとの協力に関する事実と神話
    • 米国から欧州へ:アメリカの新興企業がヨーロッパへの移転を決断する理由
    • テックオフショア開発ハブの比較:テックオフショア ヨーロッパ(ポーランド)、ASEAN(フィリピン)、ユーラシア(トルコ)
    • CTOとCIOの課題は?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • ウェブサイト利用規約

    Copyright © 2026 by The Codest. All rights reserved.

    jaJapanese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian es_ESSpanish nl_NLDutch etEstonian elGreek jaJapanese