@Wire и Service Components.
В ангуляре я давно выработал простую но эффективную архитектуру приложения.
UI <-> Components <-> Services (Singletons) <-> Backend
Компоненты отвечают только за работу с UI, никакой бизнес логики. Вся бизнес логика в Сервисах, в том числе любая логика для работы с бэкендом. Сингелтоны для того чтобы тут же в сервисах хранить данные. Короче сервисы это такие Store с бизнес логикой. Не выношу сторы отдельно, не вижу в этом смысла. Такая модель прекрасно себя зарекомендовала во многих проектах и проста в понимании любого уровня разработчиками.
Попытался замутить похожее в LWC. Благо в LWC тоже есть такое понятие как Service Components (
https://developer.salesforce.com/blogs/2 ... mponents) но мне не очень нравится как в статье это описано.
Я реализовал свой сервис компонент в таком виде
import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';
import { CommonService } from "c/commonService";
class RemoteService {
static cards = null;
static saveCards(card) {
...
}
static getCards() {
...
}
}ето
export { RemoteService }
Сервис компонент это просто JS файл в отдельной папке. Кстати IC2 умеет генерить шаблон под Service Component с классом внутри, значит я на правильном пути. Но я использую static методы и проперти внутри. Таким образом получаем singleton со своим state который можем шарить между компонентами. К тому же такий подход с классом (а не просто набросанные функции и переменные) позволяет сделать красивый и понятный вызов метода. Сразу видно откуда метод растет, а не просто вызов какой-то непонятной функции болтается
let cards = await RemoteService.getCards();
Так как сервис у нас есть, то инкапсулируем бизнес логику и общение с бэкендом. И вот тут вспылвает подводный камень.
Если к Apex Method мы можем обратиться из Сервис компонента
import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';
то использовать @wire нет. @wire работает только внутри обычного компонента который наследует LightningElement и используется в DOM. Можно конечно сервис компонент тоже сделать LightningElement с пустым template и засунуть в root component. Но какой-же это костыль

. Поэтому с моим подходом остается использовать только императивный вызов Apex (
https://developer.salesforce.com/docs/co ... perative) и реализовывать получение данных вручную. НО ЕСЛИ ЧЕСТНО мне это даже больше нравится, больше контроля над процессом работы с данными. Ненавижу магию. И то что я увидел в @wire повергло меня в шок. Конечно в самых простых случаях @wire можно использовать, но в более серьезных задачах это бомба замедленного действия. Каждый @wire отрабатывается асинхронно и в том же конструкторе connectedCallback данных еще нет. Если надо произвести какие-то манипуляции с двумя и более @wire атрибутами, то я вижу только кучу костылей. Задумка хорошая, но реализация подвела - хотя может я еще не постиг дзен Salesforce

.
Вот к примеру мне понравился этот метод
@wire(getObjectInfo, { objectApiName: CARD_OBJECT })
получаем все метаданные (объекта + полей) для любого объекта. Но опять же, могу использовать это только внутри UI компонента но не в сервисе, а также ХЗ когда оно у меня появится (надо делать handler и отлавливать событие). Бррр.
В моем с сервисами придется мутить костыли в LWC или доставать метаданные через Apex метод вручную.