Apex でのサービスレイヤーの原則の適用
学習の目的
この単元を完了すると、次のことができるようになります。
- サービス Apex クラスを作成して、アプリケーションで活用する。
- サービス Apex クラスを API として公開する。
一緒にトレイルを進みましょう
エキスパートの説明を見ながらこのステップを実行したい場合は、次の動画をご覧ください。これは「Trail Together」(一緒にトレイル) シリーズの一部です。
(この動画は 38:48 の時点から始まります。戻して手順の最初から見直す場合はご注意ください。)
サービスの作成
では、コードを確認しましょう。データベースと状態管理を完全にカプセル化している場合、1 つの実装アプローチとなるのはサービスの操作を示す静的メソッドで適切に名前を付けたクラスを使用する方法です。
クラスのメソッドは、サービスの操作を示します。これらが、環境または渡されたパラメーター経由で必要な情報にアクセスします。メソッドのロジックは、データベースを更新するか、カスタムの Apex の例外を使用してメソッドの戻り値のデータ型の情報を返し、エラーを示します。次の例は、所定の割引を一連の商談 (および存在する場合は品目) に適用するサービスを示しています。
public with sharing class OpportunitiesService { public static void applyDiscounts(Set<Id> opportunityIds, Decimal discountPercentage) { // Validate parameters if(opportunityIds==null || opportunityIds.size()==0) throw new OpportunityServiceException('Opportunities not specified.'); if(discountPercentage<0 || discountPercentage>100) throw new OpportunityServiceException('Invalid discount to apply.'); // Query Opportunities and Lines (SOQL inlined for this example, see Selector pattern in later module) List<Opportunity> opportunities = [SELECT Amount, (SELECT UnitPrice FROM OpportunityLineItems) FROM Opportunity WHERE Id IN :opportunityIds]; // Update Opportunities and Lines (if present) List<Opportunity> oppsToUpdate = new List<Opportunity>(); List<OpportunityLineItem> oppLinesToUpdate = new List<OpportunityLineItem>(); Decimal factor = 1 - (discountPercentage==null ? 0 : discountPercentage / 100); for(Opportunity opportunity : opportunities) { // Apply to Opportunity Amount if(opportunity.OpportunityLineItems!=null && opportunity.OpportunityLineItems.size()>0) { for(OpportunityLineItem oppLineItem : opportunity.OpportunityLineItems) { oppLineItem.UnitPrice = oppLineItem.UnitPrice * factor; oppLinesToUpdate.add(oppLineItem); } } else { opportunity.Amount = opportunity.Amount * factor; oppsToUpdate.add(opportunity); } } // Update the database SavePoint sp = Database.setSavePoint(); try { update oppLinesToUpdate; update oppsToUpdate; } catch (Exception e) { // Rollback Database.rollback(sp); // Throw exception on to caller throw e; } } public class OpportunityServiceException extends Exception {} }
前述の設定設計での考慮事項を適用する場合、Options パラメーターを使用して、上記サービスのオーバーロードバージョンを追加できます。これにより、コール元は、サービスに作業のコミットをスキップするように指示できます。割引済みの値を返すことにより、クライアントは適用される割引のプレビューを実装できます。
public static List<Decimal> applyDiscounts( List<Id> opportunityIds, Decimal discountPercentage, Options config)
上記の完全な例のメソッド署名は ID のリストを使用します。これも設計の考慮事項に沿っています。ただし、使用されたのは割引の 1 つのパラメーターのみでした。同一の割引がすべての商談に適用された可能性が考えられます。なお、商談ごとに異なる割引を許可する必要がある場合、次に示すパラメータークラスを使用できます。
public class OpportunityService { public class ApplyDiscountInfo { public Id OpportunityId; public Decimal DiscountPercentage; } public static void applyDiscounts(List<ApplyDiscountInfo> discInfos) { // Implementation... } }
API としてのサービスの公開
API は多くの人が利用します。API を通じて外部向けのサービスロジックをアプリケーションに公開することは、イノベーションの優れたエコシステムや、商品に関わるパートナーの協力の強化に必要不可欠です。
しっかりとテストされ、堅牢で、あらゆるクライアントに対してオープンなサービスレイヤーであれば (あなた自身が使用している以上そうだと思いますが)、クラスおよびメソッド修飾子を public
から global
に変更して、簡単にサービスを Apex 開発者に公開できます。これで、
global class OpportunityService { global class ApplyDiscountInfo { global Id OpportunityId; global Decimal DiscountPercentage; } global static void applyDiscounts(List<ApplyDiscountInfo> discInfos) { // Implementation... } }
ただし、注意すべき点もあります。AppExchange パッケージを作成する場合、global の使用は、リリース間のメソッド署名変更時に影響します。これらの影響を理解してください。
また、モバイルや IoT など、プラットフォーム外のコール元に対する API の公開も便利です。このための 1 つの方法は、REST プロトコルの使用です。カスタム Apex REST API の実装は次のとおりです。
@RestResource(urlMapping='/opportunity/*/applydiscount') global with sharing class OpportunityApplyDiscountResource { @HttpPost global static void applyDiscount(Decimal discountPercentage) { // Parse context RestRequest req = RestContext.request; String[] uriParts = req.requestURI.split('/'); Id opportunityId = uriParts[2]; // Call the service OpportunitiesService.applyDiscounts( new Set<Id> { opportunityId }, discountPercentage); } }
商談の ID を URI から取得し、提示情報から割引率を取得します。前の単元の JavaScript Remoting を使用した例のように、例外はコール元で処理します。プラットフォームが、例外を適切な JSON または XML 応答にマーシャリングします。
プロのヒント: Flow Builder のユーザーが、コードを記述することなくサービスレイヤー機能にアクセスできるように、呼び出し可能なメソッドをいくつか公開することを検討してください。優れたプラットフォームです!
リソース
- 関心の分離 (Wikipedia)
- Martin Fowler’s Service Layer Pattern (Martin Fowler のサービスレイヤーパターン)
- Martin Fowler’s Enterprise Architecture Patterns (Martin Fowler のエンタープライズアーキテクチャパターン)