Salesforce.comプラットフォームで開発するためのRestサービスの柔軟なアーキテクチャ

Salesforce.comは、海外で人気のあるCRMシステムです。 ロシアとCIS諸国では、このプラットフォームに特化した開発者は多くありませんが、労働市場には雇用機会が絶えず現れており、人々はゆっくりと確実にこのプラットフォームに移行しています。



RuNetには、Force.comプラットフォームでの開発専用のリソースはあまりありません。プラットフォームの人気を高めるために、開発に関連する記事をいくつか書きたいと思います。



最初の記事では、Restサービスを作成するための柔軟なアーキテクチャを扱います。



問題



Force.comプラットフォームは、MVCパターンを使用してアプリケーションを開発し、既存の機能をカスタマイズするように設計されています。 サーバーレンダリングの利点は否定できませんが、多くの場合、アプリケーションにより多くのダイナミズムを追加する必要があり、この場合、Restサービスを省くことができません。



すぐに使えるSalesforceの機能:



プラットフォームは、その限界でも知られています 。 そしてこの場合、それはUsage Limit APIです。 問題を取り除き、制限に触れないようにするために、次のソリューションが開発されました。



解決策



解決策は、コントローラーで通常のVisualForceページを使用してクエリ結果を取得することです。



その結果、次のコンテンツを含むページができました。



<apex:page controller="GS_RestApiController" action="{!execute}" contenttype="text/javascript" showHeader="false" sidebar="false"> <apex:outputText value="{!response}" escape="false"/> </apex:page>
      
      







ページをロードすると、 GS_RestApiControllerコントローラーのexecuteメソッドが実行されます。 そして、結果はoutputTextにバインドされます



コントローラーコード:



 public class GS_RestApiController { private static final String COMMAND_NAME_PARAM = 'command'; private Map<String, String> commandAliasNameMap = new Map<String, String>{ 'test' => 'FirstTest' }; public String response {get; private set;} public GS_RestApiController() { } public void execute() { this.response = getCommand().execute().toJSON(); } private GS_CommandContainer.GS_Command getCommand() { Map<String, String> params = ApexPages.currentPage().getParameters(); String commandName = params.get(COMMAND_NAME_PARAM); if (commandAliasNameMap.containsKey(commandName)) { commandName = commandAliasNameMap.get(commandName); } params.remove(COMMAND_NAME_PARAM); return GS_CommandFactory.create(commandName, params); } }
      
      





コントローラーでは、 コマンドパラメーターを使用して目的のコマンドを実行し、必要に応じてコマンドとそのエイリアスの対応を保存します。



すべてのコマンドはコンテナクラスに保存されます-GS_CommandContainer



 public class GS_CommandContainer { public abstract class GS_Command { private Map<String, String> params = new Map<String, String>(); public void setParams(Map<String, String> params) { this.params = params; } public GS_RestResponse execute() { try { Object resultObject = perform(); return new GS_RestResponse(GS_StatusCode.OK, getMessage(), resultObject); } catch (GS_Exception exp) { String message = exp.getMessage() + exp.getStackTraceString(); return new GS_RestResponse(GS_StatusCode.ERROR, message); } catch (Exception exp) { String message = exp.getMessage() + exp.getStackTraceString(); return new GS_RestResponse(GS_StatusCode.ERROR, message); } } public abstract Object perform(); public virtual String getMessage() { return null; } } public class GS_DefaultCommand extends GS_Command { public override Object perform() { return 'This is defult result.'; } public override String getMessage() { return 'This is default message.'; } }
      
      





したがって、新しいコマンドを追加するには、基本クラスGS_Commandを拡張し、実行ロジックが存在するperform()メソッドを実装するだけです。



GS_Commandクラスのインスタンスを作成するために、ファクトリはGS_CommandFactoryを対象としています。



 public class GS_CommandFactory { private static final String DOT = '.'; private static final String COMMAND_CONTAINER_NAME = 'GS_CommandContainer'; private static final String DEFAULT_COMMAND_NAME = 'GS_DefaultCommand'; private static final String COMMAND_NAME_TEMPLATE = 'GS_{0}Command'; private static final String COMMAND_NAME_TEMPLATE_WITH_CONTAINER = COMMAND_CONTAINER_NAME + DOT + COMMAND_NAME_TEMPLATE; private static final String DEFAULT_COMMAND_NAME_TEMPLATE_WITH_CONTAINER = COMMAND_CONTAINER_NAME + DOT + DEFAULT_COMMAND_NAME; public static GS_CommandContainer.GS_Command create() { Type commandType = Type.forName(DEFAULT_COMMAND_NAME_TEMPLATE_WITH_CONTAINER); GS_CommandContainer.GS_Command command = (GS_CommandContainer.GS_Command)commandType.newInstance(); return command; } public static GS_CommandContainer.GS_Command create(String commandName, Map<String, String> params) { if(String.isBlank(commandName)) { create(); } String commandClassName = String.format(COMMAND_NAME_TEMPLATE_WITH_CONTAINER, new String[] {commandName}); Type commandType = Type.forName(commandClassName); if(commandType == null) { commandType = Type.forName(DEFAULT_COMMAND_NAME_TEMPLATE_WITH_CONTAINER); } GS_CommandContainer.GS_Command command = (GS_CommandContainer.GS_Command)commandType.newInstance(); command.setParams(params); return command; } }
      
      





渡されたパラメーターに応じて、必要なコマンドのインスタンスを作成するか、そのようなコマンドが見つからない場合はデフォルトクラスのインスタンスを作成します。



使用例は非常に簡単です。

 var result = $.post('{!Page.RestApi}', {command : 'test'});
      
      





結果:

 {"result":"FirstTestResult + Params : {}","message":"FirstTestMessage","code":200}
      
      





パラメータなしでクエリを実行すると、デフォルトのコマンドが実行されます。 コマンド名は、GS_CommandFactoryで説明されているCOMMAND_NAME_TEMPLATEパターンと一致する必要があり、GS_RestApiControllerクラスのcommandAliasNameMapでエイリアスとコマンド名の一致を追加することもできます。



私の意見では、このアーキテクチャは便利で簡単に拡張できます。



プロジェクトのソースコードはGitHubにあります



PSこのプラットフォームでの開発に関する記事を書き続ける価値があるかどうかについて、読者からフィードバックを受け取りたいと思います。



ありがとう



All Articles