Help us understand the problem. What is going on with this article?

AWS SDK for Ruby V3のAws::S3::ClientとAws::S3::Resourceの違いに正面から向き合う

AWS SDK for Ruby V3を使おうとすると、Aws::S3::ClientAws::S3::Resourceという2つのインターフェースに出くわします。
今まで目を背けて雰囲気で使い分けていましたが、ちゃんと向き合ってみることにしました。

TL;DR

  • ClientはAPIをそのままSDK化したもの
  • ResourceはClientをラップしたもので、リソースにフォーカスして再構築している
  • たいてい同じことはできるけど、Resourceのほうが直感的、Clientのほうが細かいことができる

公式の説明

aws-sdkのgemのREADMEにありました。
https://github.com/aws/aws-sdk-ruby#resource-interfaces

Resource interfaces are object oriented classes that represent actual resources in AWS. Resource interfaces built on top of API clients and provide additional functionality. Each service gem contains its own resource interface.

(Google翻訳)
リソースインターフェイスは、AWSの実際のリソースを表すオブジェクト指向クラスです。 APIクライアント上に構築され、追加機能を提供するリソースインターフェイス。 各サービスgemには、独自のリソースインターフェイスが含まれています。

この文章から分かるように、実はS3だけでなく、ほかのサービスにもClientとResourceが存在します。EC2とかRDSとかIAMとかSQSとかSNSとかDynamoDBなどなど。ここではS3に限って説明しますが、基本的な考えは同じです。

具体的に比べてみる

指定バケットに入っているオブジェクトのキー名一覧を表示する操作で比較してみましょう。

Client

client = Aws::S3::Client.new
client.list_objects_v2(bucket: 'bucket_name').contents.each do |object|
  puts object.key
end

Resource

resource = Aws::S3::Resource.new
bucket = resource.bucket('bucket_name')
bucket.objects.each do |object|
  puts object.key
end

行数自体はClientの方が少ないですが、Resourceの方が何をしているのか直感的にわかりやすくないでしょうか?
Resouceの説明に「AWSの実際のリソースを表すオブジェクト指向クラス」とありましたが、BucketオブジェクトやObjectオブジェクトといった実際リソースを表すクラスを作って操作しています。人間にとっては分かりやすいです。
Clientはlist_objects_v2の返り値に対してcontentsなんてわけのわからないものを挟んでますが、こんなのいちいち返り値を確認しないと書けないわけで、ちょっと使いづらい。

Clientの存在意義

じゃあもうResourceだけでいいじゃん、ってなりそうですが、Clientにも存在意義はあります。

コードを見てもらえれば分かると思いますが、ClientはAPIをそのままSDK化したものです。
https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-s3/lib/aws-sdk-s3/client.rb

すべてのメソッドが同じ形で2行で終わってるのが分かると思います。
たとえばlist_bucketsはこんな感じです。

    def list_buckets(params = {}, options = {})
      req = build_request(:list_buckets, params)
      req.send_request(options)
    end

send_requestを見てみると、

    def build_request(operation_name, params = {})
      handlers = @handlers.for(operation_name)
      context = Seahorse::Client::RequestContext.new(
        operation_name: operation_name,
        operation: config.api.operation(operation_name),
        client: self,
        params: params,
        config: config)
      context[:gem_name] = 'aws-sdk-s3'
      context[:gem_version] = '1.48.0'
      Seahorse::Client::Request.new(handlers, context)
    end

ということで、Seahorse::Client::Request.new(handlers, context)の部分でAPIを呼んでいます。

Resourceのほうが便利で分かりやすいですが、Clientの方がAPIに近いので細かいことができます。
マルチパートアップロードを例に取ると、Resourceは1つのメソッドにまとまっていますが、Clientはマルチパートアップロードを開始、パートのアップロード、マルチパートアップロードの完了/中止と、複数のメソッドを組み合わせて実現することになります。書くのは大変ですが、間に独自処理を入れたりできそうですね。

まとめ

好きな方を使えばいいと思います╭( ・ㅂ・)و ̑̑

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away