Skip to main content

Apex での作業単位の原則の適用

学習の目的

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

  • 作業単位クラスとそのメソッドを記述する。
  • Apex で fflib_SObjectUnitOfWork クラスとその API を利用する。
メモ

メモ

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

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

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

エキスパートの説明を見ながらこのステップを実行したい場合は、次の動画をご覧ください。これは「Trail Together」(一緒にトレイル) シリーズの一部です。

(この動画は 1:08:31 の時点から始まります。戻して手順の最初から見直す場合はご注意ください。)

作業単位による効率化

これは高度なコンテンツであるため、複雑なシナリオで確認しましょう。商談とすべての必須の連動レコードをゼロから作成するという量の多い作業を行います。次のテスト設定コードは、これを作業単位なしで完了する方法を示しています。長いと思いませんか?

List<Opportunity> opps = new List<Opportunity>();
List<List<Product2>> productsByOpp = new List<List<Product2>>();
List<List<PricebookEntry>> pricebookEntriesByOpp = new List<List<PricebookEntry>>();
List<List<OpportunityLineItem>> oppLinesByOpp = new List<List<OpportunityLineItem>>();
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'Opportunity ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    opps.add(opp);
    List<Product2> products = new List<Product2>();
    List<PricebookEntry> pricebookEntries = new List<PricebookEntry>();
    List<OpportunityLineItem> oppLineItems = new List<OpportunityLineItem>();
    for(Integer i=0; i<o+1; i++) {
        Product2 product = new Product2();
        product.Name = opp.Name + ' : Product : ' + i;
        products.add(product);
        PricebookEntry pbe = new PricebookEntry();
        pbe.UnitPrice = 10;
        pbe.IsActive = true;
        pbe.UseStandardPrice = false;
        pbe.Pricebook2Id = Test.getStandardPricebookId();
        pricebookEntries.add(pbe);
        OpportunityLineItem oppLineItem = new OpportunityLineItem();
        oppLineItem.Quantity = 1;
        oppLineItem.TotalPrice = 10;
        oppLineItems.add(oppLineItem);
    }
    productsByOpp.add(products);
    pricebookEntriesByOpp.add(pricebookEntries);
    oppLinesByOpp.add(oppLineItems);
}
// Insert Opportunities
insert opps;
// Insert Products
List<Product2> allProducts = new List<Product2>();
for(List<Product2> products : productsByOpp) {
    allProducts.addAll(products);
}
insert allProducts;
// Insert Pricebooks
Integer oppIdx = 0;
List<PricebookEntry> allPricebookEntries = new List<PricebookEntry>();
for(List<PriceBookEntry> pricebookEntries : pricebookEntriesByOpp) {
    List<Product2> products = productsByOpp[oppIdx++];
    Integer lineIdx = 0;
    for(PricebookEntry pricebookEntry : pricebookEntries) {
        pricebookEntry.Product2Id = products[lineIdx++].Id;
    }
    allPricebookEntries.addAll(pricebookEntries);
}
insert allPricebookEntries;
// Insert Opportunity Lines
oppIdx = 0;
List<OpportunityLineItem> allOppLineItems = new List<OpportunityLineItem>();
for(List<OpportunityLineItem> oppLines : oppLinesByOpp) {
    List<PricebookEntry> pricebookEntries = pricebookEntriesByOpp[oppIdx];
    Integer lineIdx = 0;
    for(OpportunityLineItem oppLine : oppLines) {
        oppLine.OpportunityId = opps[oppIdx].Id;
        oppLine.PricebookEntryId = pricebookEntries[lineIdx++].Id;
    }
    allOppLineItems.addAll(oppLines);
    oppIdx++;
}
insert allOppLineItems;

では、作業単位パターンを使用して、上記のコードを書き直しましょう。まず、作業単位インスタンスの作成から開始します。コンストラクターに渡されるオブジェクトを連動関係の順序にして、commitWork メソッドが正しい順序でそれらを挿入できるようにする必要があります。

// Create a Unit Of Work
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(
    new Schema.SObjectType[] {
        Product2.SObjectType,
        PricebookEntry.SObjectType,
        Opportunity.SObjectType,
        OpportunityLineItem.SObjectType
    }
);

前の単元では、applyDiscount サービスメソッドは、registerDirty メソッドを使用して、更新する商談および商談品目レコードを登録しました。registerNew メソッドは、新しいレコードを挿入します。また、子レコードが挿入される場合、このメソッドが、正しい親 ID が適用されていることを挿入前に確認します。これは、22 行目のアクションで確認できます。その他のリレーションを作成する場合、registerRelationship メソッドをコールすることもできます (21 行目)。では、作業単位に作業させましょう! と、ダジャレはこれくらいにしておきますね。

// Do some work!
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'UoW Test Name ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    uow.registerNew(opp);
    for(Integer i=0; i<o+1; i++) {
      Product2 product = new Product2();
      product.Name = opp.Name + ' : Product : ' + i;
      uow.registerNew(product);
      PricebookEntry pbe = new PricebookEntry();
      pbe.UnitPrice = 10;
      pbe.IsActive = true;
      pbe.UseStandardPrice = false;
      pbe.Pricebook2Id = Test.getStandardPricebookId();
      uow.registerNew(pbe, PricebookEntry.Product2Id, product);
      OpportunityLineItem oppLineItem = new OpportunityLineItem();
      oppLineItem.Quantity = 1;
      oppLineItem.TotalPrice = 10;
      uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
      uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
    }
}

前の単元で説明したとおり、これらのメソッドは、データベース操作を実行しません。操作を実行するには、commitWork メソッドで作業をコミットする必要があります。これは、適切な子から親の順序で登録されたレコードで正しい DML ステートメントをコールします。また、リレーションについては、親 ID を子レコードに割り当てて、データベースに挿入するようにオブジェクトモデルを効果的にまとめます。

// Commit the work to the database!
uow.commitWork();

コードが短いだけでなく、ややこしいリストが含まれておらず、ロジック面の内容を確認しやすくなっています。使用できるその他の登録メソッドの例は、次のとおりです。

 // Inserts new Opportunity when committing
uow.registerNew(opp);
// Inserts new Opportunity Line Item and associates it with the given Opportunity record when committing
uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
// Relates the given Opportunity Line Item to the given Price Book Entry when committing
uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);

まとめ

このモジュールでは、独自の考慮事項と関心を持つレイヤーで、アプリケーション実装を考える利点について学習しました。生き物のように、それぞれが、アプリケーションを堅牢にして、長期間にわたり持続するようにするための役割を担っています。

アプリケーションの心臓であるビジネスロジックを分離することで、この最初の一歩を踏み出しました。ここで終了することも、引き続きアプリケーションに必要なオブジェクト動作 (トリガーコード) や情報のクエリに対して関心の分離を適用することもできます。「Apex エンタープライズパターン: ドメインおよびセレクターレイヤー」モジュールに続いて、ドメインおよびセレクターアプリケーションレイヤーについてもご確認ください。そして、サービスの構築を楽しんでください!

課題に向けた準備

これらの課題を完了するために、いくつかのオープンソースライブラリをリリースする必要があります。fflib_SObjectUnitOfWork クラスは、ApexMocks Framework オープンソースライブラリに従属する Apex Common オープンソースライブラリに含まれています。まず ApexMocks、次に Apex Common をインストールする必要があります。これらのライブラリとそれぞれのオープンソース使用許諾契約についての詳細は、各リポジトリを参照してください。

ライブラリを組織にインストールするには、次のリリースボタンを使用します。

ApexMocks オープンソースライブラリのリリース。

Deploy to Salesforce (Salesforce にリリース)

Apex Common オープンソースライブラリのリリース。

Deploy to Salesforce (Salesforce にリリース)

リソース

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

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

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