AWS SDK for Ruby V3を使おうとすると、Aws::S3::Client
とAws::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はマルチパートアップロードを開始、パートのアップロード、マルチパートアップロードの完了/中止と、複数のメソッドを組み合わせて実現することになります。書くのは大変ですが、間に独自処理を入れたりできそうですね。
まとめ
好きな方を使えばいいと思います╭( ・ㅂ・)و ̑̑