Skip to main content

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

学習の目的

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

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

メモ

日本語で受講されている方へ
Challenge は日本語の Trailhead Playground で開始し、かっこ内の翻訳を参照しながら進めていってください。Challenge での評価は英語データを対象に行われるため、英語の値のみをコピーして貼り付けるようにしてください。日本語の組織で Challenge が不合格だった場合は、(1) この手順に従って [Locale (地域)] を [United States (米国)] に切り替え、(2) [Language (言語)] を [English (英語)] に切り替えてから、(3) [Check Challenge (Challenge を確認)] ボタンをクリックしてみることをお勧めします。

翻訳版 Trailhead を活用する方法の詳細は、自分の言語の Trailhead バッジを参照してください。

一緒にトレイルを進みましょう

エキスパートの説明を見ながらこのステップを実行したい場合は、次の動画をご覧ください。これは「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 のユーザーが、コードを記述することなくサービスレイヤー機能にアクセスできるように、呼び出し可能なメソッドをいくつか公開することを検討してください。優れたプラットフォームです!

リソース

Salesforce ヘルプで Trailhead のフィードバックを共有してください。

Trailhead についての感想をお聞かせください。[Salesforce ヘルプ] サイトから新しいフィードバックフォームにいつでもアクセスできるようになりました。

詳細はこちら フィードバックの共有に進む