field service で実際にやらかした判断の話だ。当時、Field Service Mobile (FSM) アプリの導入プロジェクトで、作業指示の詳細画面に表示するカスタムのチェックリストや記録入力用のLWCを開発していた。
最初の判断ミスは、「FSMアプリがオフライン対応しているなら、カスタムLWCもそれに乗っかって賢く動いてくれるだろう」と楽観視したことだ。FSMアプリ自体が膨大なデータをオフラインで扱えるわけだから、LWCがちょっとしたデータ更新や参照をするくらい、何とかなるだろうと高を括っていた。
「オフラインキャッシュ」の魔力と落とし穴
我々は、LWC内で作業指示に紐づくカスタムオブジェクトのレコードを読み書きする設計にした。最初はLWCから直接Apexを呼び出して、データを取得・更新していた。開発環境では問題なく動く。しかし、テストチームから「オフライン時にLWCが表示されない」「データが更新できない」という報告が頻繁に上がるようになった。まさか、と思ったよ。
原因は、Salesforceのオフラインキャッシュに対する理解不足だった。FSMの標準機能でオフラインデータキャッシュは非常に強力だが、それは「Salesforceが管理する標準UIや、設定されたオブジェクト・フィールド・関連リスト」に限定される。カスタムLWCがApex経由でデータを取得する場合、そのApexが参照するオブジェクトやフィールドが、明示的にオフラインデータキャッシュの対象として設定されていないと、オフライン時には何も取得できないのだ。
特に我々がハマったのは、カスタムオブジェクトの特定のフィールドや、関連する別のオブジェクトのレコードをApexで取得しようとした時だ。例えば、Work OrderからWork Order Line Itemを辿り、さらにそのWork Order Line Itemに紐づくCustom_Checklist__cというオブジェクトを参照する、といった複雑なデータ階層を持っていた。Offline Field Service SettingsのOffline Data Cacheで、Work OrderとWork Order Line Itemは設定していたが、その先のCustom_Checklist__cが完全に抜け落ちていた。これではLWCがApex経由でデータを取得しようとしても、オフライン時はキャッシュに存在しないため、Apexのクエリが空っぽの結果を返すか、最悪の場合エラーになった。
// 当時のApexメソッドのイメージ (オフライン考慮が甘かった例)
public with sharing class ChecklistService {
@AuraEnabled(cacheable=true)
public static List<Custom_Checklist__c> getChecklistsForWorkOrderLineItem(Id woliId) {
// オフラインキャッシュ設定が不十分だと、woliIdの関連レコードが全く取れない
// UI APIを使わず直接ApexでSOQLを叩く場合、Apexが参照する全てのオブジェクト・フィールドが
// Offline Data Cacheで設定されていないと、オフラインではデータが取得できない。
return [SELECT Id, Name, Status__c, Value__c FROM Custom_Checklist__c WHERE WorkOrderLineItem__c = :woliId];
}
@AuraEnabled
public static String updateChecklistStatus(Id checklistId, String newStatus) {
Custom_Checklist__c checklist = [SELECT Id, Status__c FROM Custom_Checklist__c WHERE Id = :checklistId];
checklist.Status__c = newStatus;
update checklist;
return 'Success';
}
}
これは本当に痛い判断ミスだった。FSMがオフライン対応する、という認識はあっていたが、その対応が「どのように」なされるか、特に「カスタム開発部分」でどれだけ配慮が必要か、という解像度が低すぎたのだ。
今なら別の選択をする:UI APIとオフラインキャッシュの徹底活用
今なら、まずはLWCから直接UI API (`getRecord`, `getRecords`, `updateRecord`) を利用することを第一に考える。UI APIはFSMのオフラインキャッシュと密接に連携しており、Apexを介さずにLWCが直接レコードを操作する方が、オフライン時の挙動が安定しやすい。特に、単一レコードや関連リストの表示であれば、@wireデコレータとUI APIを使えば、FSMが自動でキャッシュを管理してくれる。これは当時、Apexを使えば何でもできるという開発者の悪い癖が出ていたと反省している。
もしどうしてもApexが必要な場合は、Apexが参照するすべてのオブジェクトとフィールド、関連リストを、Offline Field Service SettingsのOffline Data Cacheで徹底的に設定する。関連オブジェクトの階層が深ければ深いほど、忘れずに設定する必要がある。これを見落とすと、オフライン時に突然LWCが沈黙する、という悪夢が繰り返される。
あの時のプロジェクトでは、最終的にLWC内でオフライン時のデータハンドリングロジックを相当量書き加える羽目になった。具体的には、オフライン時はApexを叩かず、Service Reportのような標準オブジェクトから取得できる情報を頼りに画面を構成したり、更新はローカルストレージに一時保存し、オンライン復帰後にまとめて同期する、といった泥臭い対応だ。この同期ロジックがまた複雑で、競合発生時の解決策まで考えることになり、かなりの時間とコストを浪費した。
外部API連携も同様で、オフライン時は外部APIは当然呼べない。これを考慮せず、LWCからリアルタイムに外部APIを呼び出す設計をしたため、オフライン時のUXが著しく損なわれた。当時、オフラインでのデータ連携については、もっと慎重に、そして悲観的に設計すべきだった。オフライン時は別の画面を表示するか、前回同期時のデータを使うか、あるいは外部連携が不要な業務フローに切り替えるか、といったフォールバックプランを明確にするべきだった。
これは当時の自分向けのメモだ。
コメント
コメントを投稿