アグリゲーターアーキテクチャ:Webサービスパターン(パート1)

今日、多くのWebアプリケーションとサービスが作成されており、それらの目標は同じですが、実行方法が異なります。 情報はネットワーク全体に分散しているため、ユーザーは仕事の効果を高めるために、多くの同様のサービスにアクセスする必要があります。 たとえば、顧客は入札サイトにタスクを配置したいと考えています。 提出されたアプリケーションの数を増やすために、彼は反復的な作業に時間を費やしています:さまざまなフリーランスの交換で、オファーの作成とプロジェクトデータの入力。 この問題を解決しようとしているアグリゲーターサイトが表示されますが、アグリゲーターのトピックに関する新しいサービスの出現により、そのサポートはますます困難になっています。 すべての新しい機能と、サービスごとに異なるデータ構造を統合する必要があります。 幸いなことに、そのようなものを作成して維持するのは私たちが最初ではありません。そのようなアプリケーションのサポートを簡素化し、柔軟なアーキテクチャを作成できるパターンがすでにあります。 この記事では、Odesk、Freelancer、Elanceなどのフリーランサー向けの入札サイトを結合できるアグリゲーターアーキテクチャの例を紹介します。



開発者が直面する主な問題:



  1. サービスを操作するさまざまな原則:APIを提供するものもあれば、ボットを介して作業する必要があるものもあります-人間の行動を模倣します。
  2. サービスからの応答の異質性-jsonを返すもの、xmlを返すもの、Webページを返すものがあります。
  3. 2から派生-データ構造の不均一性-たとえば、Projectオブジェクトの構造

    cフリーランサーは次のようになります。

    <?xml version="1.0" encoding="UTF-8"?> <xml-result xmlns="http://api.freelancer.com/schemas/xml-0.1"> <id>588582</id> <name>sign design web software</name> <url>http://www.sandbox.freelancer.com/projects/PHP-ASP/sign-design-web-software.html</url> <buyer> <url>http://www.sandbox.freelancer.com/users/1353095.html</url> <id>1353095</id> <username>billk89</username> </buyer> <short_descr>Create and upload sign design web software for our client nlsigndesign.com Public internet users must be able to create there own signs online, then save and submit as a file. </short_descr> <jobs> <job>PHP</job> </jobs> ... </xml-result>
          
          





    Odeskは次のように応答します。

      <response> .... <profile> .... <buyer>  <cnt_assignments>0</cnt_assignments>  <op_contract_date>December 18, 2011</op_contract_date>  <timezone>Russia (UTC+06)</timezone> </buyer> <op_title>A social network client app for iPhone/iPad</op_title> <ciphertext>~~05d405f2d5b8eb27</ciphertext> .... <op_required_skills>  <op_required_skill>   <skill>ipad,ui-design,iphone-development</skill>  </op_required_skill> </op_required_skills> .... <op_desc_digest> Hello, We need a social network client app for iPhone/iPad to be developed. It should support Facebook, Twitter and Linked-In. </op_desc_digest> .... </profile> </response>
          
          







理解を容易にするため、個別の記事で各問題の解決策を説明することにしました。 この記事では、OdeskとFreelancerの2つの人気のあるフリーランスエクスチェンジの例で、さまざまなサービスと作業を統合する方法を示します。 ソースコードはYiiフレームワークを使用してPHP5で記述されています。



インターフェースを作成する



したがって、最初に行う必要があるのは、各具象クラスの実装を非表示にして、さまざまなサービスのメソッドの単一セットを呼び出せるようにするためのインターフェースを作成することです。



 interface ServiceInterface { public function authorize(); public function getServiceName(); public function setCredential(ModelCredential $credential); public function searchProjects(); }
      
      







サービスアダプタを作成します



さらに、サービスごとに、このインターフェイスを実装するサービスアダプター(Webサービスに適用される「アダプター」パターンのバリエーション)を作成します。 どちらのクラスもAPIを介してサービスを処理しますが、呼び出しの実装の違いを隠します。



フリーランサーサービスアダプター:

 class FreelancerService implements ServiceInterface { private $_serviceName = 'freelancer'; private $_service = null; public function __construct(ModelCredential $credential = null) { Yii::import('ext.freelancer-api-wrapper.*'); $this->_service = new Freelancer(Yii::app()->params['freelancer']['token'],Yii::app()->params['freelancer']['secret'], 'http://geeks-board.local/authorizeService/freelancer/?'); if($credential !== null) { $this->setCredential($credential); } } public function authorize() { if(!isset($_GET['oauth_token'])) { $requestToken = $this->_service->requestRequestToken(); $redirectUrl = $this->_service->getRedirectUrl($requestToken); header('Location: ' . $redirectUrl); } else { $oauth_verifier = $this->_service->getRequestTokenVerifier($_GET['oauth_token']); $auth = $this->_service->requestAccessToken($oauth_verifier,$_GET['oauth_token']); $this->_service->oauth->setToken($auth['oauth_token'],$auth['oauth_token_secret']); $this->_service->auth = $auth; return $auth; } return false; } public function getServiceName() { .... } public function setCredential(ModelCredential $credential) { .... } public function searchProjects() { .... } }
      
      







Odesk Service Adapter:

 class OdeskService implements ServiceInterface { private $_serviceName = 'odesk'; private $_service = null; public function __construct(ModelCredential $credential = null) { Yii::import('ext.odesk-api.*'); Yii::import('classes.mapper.service.RequestMapper'); $this->_service = new oDeskAPI(Yii::app()->params['odesk']['secret'], Yii::app()->params['odesk']['token']); if($credential !== null) { $this->setCredential($credential); } } public function authorize() { return $this->_service->auth(); } public function getServiceName() { .... } public function setCredential(ModelCredential $credential) { .... } public function searchProjects() { .... } }
      
      







両方のクラスがServiceInterfaceインターフェースを実装しますが、authorizeメソッドの実装はそれぞれ異なることに注意してください。 また、承認のためのデータを保存するModelCredentialモデルについて言及する必要があります。 Oauth認証では、これはトークンと秘密です。



ファクトリーを作成する



将来、既存のクラスのコードを変更せずに新しいサービスを簡単に追加する必要があります( OCPの原則に従います)。 これを行うには、 「Factory Method」パターンを使用します。



 class ServiceFactory { public static function create($serviceName, $credentials = null) { $className = ucfirst(strtolower($serviceName)).'Service'; $serviceObject = Yii::createComponent($className, $credentials); if(!($serviceObject instanceof ServiceInterface)) { throw new Exception('Not an instance of Service'); } return $serviceObject; } }
      
      







また、このクラスがServiceInterfaceを実装することを確認しました。 ここでインターフェイスが使用され、サービスクラスによる実装のチェックが行われるのはなぜですか? 開発者が間違いを犯し、インターフェイスメソッドの実装を忘れた場合、phpは原則としてコードの実行を許可しません。 これにより、メソッドが実装されているという確信が得られます。 また、システムが機能するために必要な特定の方法を理解することもできます。 この機会に、私は私の話を共有します。

私が取り組んだプロジェクトの1つで、タスクはGoogle+のアダプターサービスを実装するように設定されました。 FacebookおよびTwitterアダプターはすでにプロジェクトに統合されています。 そのうちの1つのクラスを開いたとき、内部のコードの量に恐怖を覚えました。 サービスが機能するためにどのメソッドを実装する必要があるか、どのメソッドがセカンダリであるかを理解できませんでした。 いくつかのクラスを比較する必要がありました。このコードを書いた開発者に確認してください。 しばらく時間がかかりました。 このようなサービスアダプター用のインターフェイスが1つあれば、どのメソッドを作成する必要があるかがすぐにわかります。



すべてをまとめる



そのため、サービスアダプタのクラスとそれらを作成するファクトリを用意しました。 これらの部分がどのように連携するかを見てみましょう。



  Yii::import('application.models.ModelCredential'); //       -   ,    Credentials -   //     . $credentialsRecords = ModelCredential::model()->findAllByAttributes(array( 'type' => 'public' )); if(!empty($credentialsRecords)) { Yii::import('classes.service.ServiceFactory'); $aggregatedProjects = array(); foreach($credentialsRecords as $serviceCredential) { try { //          credentials. $service = ServiceFactory::create($serviceCredential->service, $serviceCredential); $foundProjects = $service->searchProjects(); //       if(is_array($foundProjects)) $aggregatedProjects = array_merge($aggregatedProjects,$foundProjects); } catch(Exception $e) { print_r($e); } } } else { echo 'empty '. "\n"; }
      
      







まとめ



このアーキテクチャにより、新しいサービスをより透過的に統合でき、開発者はメソッド名や呼び出しの違いを覚える必要がありません。



次の記事では、返された応答とデータ構造の不均一性をどうするかを説明します。



PS



Yii :: createComponent()を呼び出すことで、ファクトリコードが簡素化されました。 エクスタジに感謝します



All Articles