PHPのHTTPクライアントライブラリであるGuzzleを用いて、リトライ処理をテスタブルに実装する方法を紹介します。
Proxyパターンを使ってコードをすっきりとまとめられました。
背景
外部APIとの通信において、一時的なエラーやレート制限(HTTPステータスコード429)に対処するためにリトライ処理は欠かせません。
しかし、リトライ処理を適切に実装し、かつテスト可能な状態に保つことは簡単ではありません。
たとえば、SlackのAPIはわりと簡単にレート制限が発生するので、リトライ処理の実装は必須になるケースが多いでしょう。
ソースコードはGitHubに公開しています。
https://github.com/jnkmtsd/php-playground/tree/v0.1.2
Proxyパターンの活用
Proxyパターンを使うことで、一般的に使われるクライアント\GuzzleHttp\Client
と利用側の間にリトライ機能が追加されたオブジェクトを透過的に割り込ませられます。
それにより、コードの再利用性とテスタビリティを向上させます。
クラス図
具象クラスの依存はClientInterface
のみなので、具象クラス間の関係を理解するのは難しいかもしれません。
後述のクライアントコードを見るとわかりますが、\Pp\RetryableClient
は\GuzzleHttp\Client
をラップしていて、\Pp\Client
がそれを使用するという関係です。
リトライ処理の実装
まずはRetryableClient
から。
|
|
重要な点は以下です。
ClientInterface
で規定されたメソッドを拡張するのがProxyパターンの特徴- 規定されたメソッドを拡張せずそのまま委譲して、別メソッドを追加するのはDecoratorパターンなので、違いに注意
- コンストラクタでリトライ処理を追加
send()
などのリクエスト送信はリトライ処理が組み込まれた$client
が処理
次にClient
。
|
|
重要な点は以下です。
- コンストラクタの引数の型は
ClientInterface
にしておいてDIコンテナなどでRetryableClient
を注入するという形が基本- 型を
RetryableClient
にしてリトライ処理を強制するのも状況によってはありだと思う
- 型を
最後にクライアントコード。
|
|
\Pp\Client
インスタンスの作成は本来DIコンテナに任せるものなので、本インスタンスの生成方法はあくまで簡易的な例だと思ってください。
テストコード
以下のように、HandlerStack
にMockHandler
をpush()
することで任意のレスポンスを返すようにしてテストができます。
|
|
まとめ
Proxyパターンを活用することで、Guzzleのクライアントにリトライ機能を柔軟に追加し、テストもしやすい構成にすることができました。
このアプローチにより、コードの再利用性とメンテナンス性が向上し、より堅牢なAPIクライアントを実装できます。