進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

Apex でのサービスレイヤの原則の適用

学習の目的

この単元を完了すると、次のことができるようになります。

  • サービス Apex クラスを作成して、アプリケーションで活用する。
  • サービス Apex クラスを API として公開する。

サービスの作成

では、コードを確認しましょう。データベースと状態管理を完全にカプセル化している場合、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 応答にマーシャリングします。

プロのヒント: プロセスビルダーおよびフローツールのユーザが、コードを記述することなくサービスレイヤ機能にアクセスできるように、呼び出し可能なメソッドをいくつか公開することを検討してください。優れたプラットフォームです!

リソース