REST APIからGraphQLへの移行戦略 - 準備作業(ルビー・オン・レールズ)

REST APIからGraphQLへの移行戦略 - 準備作業(ルビー・オン・レールズ)
目次
  1. 準備作業
  2. APIのGraphQL版に含まれるコントローラとアクションの一覧
  3. コントローラのアクションを抽出してオブジェクトに変換する
  4. テストカバレッジを更新する
  5. 結論

この記事は前回の記事に続くもので、REST APIからGraphQLへの移行をテーマにしています。

前回の記事では、この移行を行うために実施したアクションプランを概説しました。

この投稿では準備段階をより詳しく見ていきます。まず、簡単なおさらい:

準備作業

  • APIのGraphQL版に含まれるコントローラとアクションを一覧化する。
  • コントローラのアクションを抽出してオブジェクトに変換する(デザインパターン command)。
  • オプション(強く推奨!):テストカバレッジを更新する。

APIのGraphQL版に含まれるコントローラとアクションの一覧

このフェーズでは、あなた自身がAPIの利用者によってどのアクションが使用されているかを一覧化できる唯一の人物です。

方法論としては、全てのコントローラを網羅的に確認する必要があります。

また、この機会にリファクタリングの予定作業やユニットテストの作成などのメモを取ると良いでしょう。これにより、今後のステップに費やす時間をより正確に見積もることができます。

コントローラのアクションを抽出してオブジェクトに変換する

まずはデザインパターン Command(または Service Object)を確認することをお勧めします。

このステップの目的は、各コントローラのアクションをオブジェクトとして抽出することです。この方法を採ることで、いくつかの利点が得られます:

  • コードの依存関係を特定できる;
  • コントローラの文脈から独立してロジックをより簡単にテストできる;
  • コードを複数の場所で再利用できる;

以下の例では、posts_controller.rb の create アクションのロジックを抽出します。

リファクタリング前:

## app/api/v1/posts_controller.rb

module Api
  module V1
    class PostsController < ::Api::ApplicationController
      def create
        ## Logique métier
        ## ...
        ## ...

        if post_repository.save(post)
          redirect_to post_path(post)
        else
          render "new"
        end
      end

      def post_repository
        @post_repository ||= ::Repositories::Post.new
      end
    end
  end
end

リファクタリング後:

## lib/command/post/create.rb

module Command
  module Post
    class Create
      def self.exec({ attrs, callbacks, repositories })
        new(callbacks, repositories).exec(attrs)
      end

      def exec(attrs)
        ## Logique métier
        ## ...
        ## ...

        if @repositories[:post].save(post)
          @callbacks[:success].call(post: post)
        else
          @callbacks[:failure].call(post: post)
        end
      end

      private

       def initialize(callbacks, repositories)
         @callbacks = callbacks
         @repositories = repositories
       end

    end
  end
end
## app/api/v1/posts_controller.rb

module Api
  module V1
    class PostsController < ::Api::ApplicationController
      def create
        ::Command::Post::Create.exec(
          {
            attrs: params,
            callbacks: {
              success: ->(args) {
                @post = args[:post]
                redirect_to post_path(@post)
              },
              failure: ->(args) {
                @post = args[:post]
                render "new"
              },
            },
            repositories: {
              post: ::Repositories::Post.new,
            }
          }
        )
      end
    end
  end
end

さらに詳しく知るための参考リンク:

テストカバレッジを更新する

変更やリファクタリングを行う前に、テストを書き、更新することをお勧めします。これにより安心して大きな変更を加えることができます。

ユニットテストの設定について詳述はしませんが、変更に合わせた例を紹介します。

rspec を使用している場合、以下の例は Command オブジェクトのテスト用テンプレートとして利用できます。

require 'rails_helper'

RSpec.describe ::Command::Post::Create do
  let (:callbacks) {
    {
      success: -> (*args) { :success },
      failure: -> (*args) { :failure }
    }
  }

  before do
    post_repository = double('::Repositories::Post')
    allow(post_repository).to receive(:save).with(an_instance_of(::Post)).and_return(true)

    @repositories = {
      post: post_repository,
    }
  end

  let (:command_post_create) { ::Command::Post::Create }

  describe "create" do
    context "the record is created" do
      let (:attrs) {
        {
       title: "Test"
    }
      }

      it "calls success callback" do
        result = command_post_create.exec(
         attrs: attrs,
         callbacks: callbacks,
         repositories: @repositories
        )

        expect(result).to eq :success
      end
    end
  end
end

さらに詳しく知るための参考リンク:

結論

この作業を終えると、コントローラのアクションからビジネスロジックを専用のオブジェクトに分離できているはずです。これらのオブジェクトは依存関係が明確に分かれており、適切にテストできるようになります。

これらのオブジェクトはプロジェクト内のどこでも再利用できるため、2つのAPIバージョン間でコードを重複させるのを避けることができます。

これにより、可読性と構造化の面で大きな価値が得られます。

タグ

  • グラフキューエル

  • REST

  • ルビー

  • API

この記事は

コメント

読み込み中...

REST APIからGraphQLへの移行戦略 - 準備作業(ルビー・オン・レールズ) | DEMILY Clément