Orbito - Dokumentacja modułówOrbito - Dokumentacja modułów
Wprowadzenie
Pierwsze kroki
API
Komunikacja
Widoki
Wdrożenie
Społeczność
For AI agents
Wprowadzenie
Pierwsze kroki
API
Komunikacja
Widoki
Wdrożenie
Społeczność
For AI agents
  • Komunikacja między modułami

    • Komunikacja między modułami
    • Core.callModule – bezpośrednie wywołania
    • Hooki – komunikacja luźno powiązana

Hooki – komunikacja luźno powiązana

System hooków to mechanizm wzorca publish-subscribe zaimplementowany w Orbito. Jeden moduł ogłasza zdarzenie (execHook), a wszystkie moduły, które się na nie zapisały (registerHook), otrzymują wywołanie i mogą zwrócić wynik. Wywołujący dostaje zebrane odpowiedzi od wszystkich nasłuchujących modułów.

To podejście pozwala rozszerzać zachowanie platformy bez modyfikacji istniejących modułów – wystarczy zainstalować nowy moduł, który rejestruje obsługę danego hooka.

Core.execHook – wywołanie zbiorcze

Wywołuje wszystkie moduły zarejestrowane na dany hook i zwraca tablicę ich odpowiedzi.

const results = await Core.execHook(hookName, data?)
ParametrTypOpis
hookNamestringNazwa hooka
dataanyOpcjonalne dane przekazywane do każdego handlera

Hook dosięga wyłącznie modułów tej samej firmy – platforma ustala kontekst na podstawie tożsamości połączenia, więc companyId nie jest przekazywane. Jeśli firma ma kilka instalacji tego samego modułu, hook trafia do każdej z nich, a odpowiedzi wszystkich instancji lądują w zwracanej tablicy.

Przykład – moduł zamówień zbiera dostępnych przewoźników:

const { Core, Log } = require('@orbito/api-lib');

Core.registerMethod('shipping.list', async (params, ctx) => {
  // Wywołaj wszystkie moduły obsługujące przewoźników
  const carriers = await Core.execHook(
    'core.shipping.getCarrier',
    { order: params.order }
  );

  // carriers to tablica odpowiedzi od wszystkich zainstalowanych modułów
  // Odfiltruj null (moduły, które nie obsługują danego zamówienia)
  const available = carriers.filter(Boolean);

  Log.info('Pobrano przewoźników', { count: available.length });

  return available;
});

Core.registerHook – nasłuchiwanie na hook

Rejestruje moduł jako implementatora danego hooka. Wywoływany gdy inny moduł wyemituje ten hook przez execHook.

Core.registerHook(hookName, handler)
ParametrTypOpis
hookNamestringNazwa hooka, na który moduł reaguje
handlerFunctionAsynchroniczny handler

Sygnatura handlera:

async (data) => { ... }

Handler dostaje dokładnie te argumenty, które wywołujący przekazał do execHook – nic więcej. Kontekst wywołania nie jest argumentem, bo liczbę argumentów hooka ustala wywołujący; trafia do handlera jako this (patrz Kto wywołał hook).

Przykład – moduł integracji DPD implementuje hook przewoźnika:

const { Core, Log } = require('@orbito/api-lib');
const ConfigSchema = require('./schemas/config');

Core.registerHook('core.shipping.getCarrier', async ({ order }) => {
  // Pobierz konfigurację DPD (automatycznie w kontekście firmy modułu)
  const config = await ConfigSchema.findOne({});

  // Jeśli moduł nie jest skonfigurowany lub włączony, zwróć null
  if (!config?.enabled) return null;

  // Oblicz cenę na podstawie wagi paczki
  const price = order.weight > 30 ? config.heavyRate : config.flatRate;

  return {
    carrierId: 'dpd',
    label: 'DPD Kurier',
    price,
    estimatedDays: 1
  };
});

Log.info('Moduł DPD załadowany');

Kto wywołał hook

Firma może mieć kilka instalacji tego samego modułu – np. trzy sklepy Prestashop, z których każdy zgłasza zamówienia tym samym hookiem. Odbiorca musi więc wiedzieć nie tylko co się stało, ale też gdzie.

Platforma dokłada do każdego wywołania tożsamość wywołującego. Nie jest ona argumentem handlera (liczba argumentów hooka należy do wywołującego) – trafia jako this:

Core.registerHook('event:order:created', async function (order) {
  // this.caller = { instanceId, moduleCode, instanceName } albo null
  if (!this.caller) {
    // hook odpalony przez platformę (akcja użytkownika, automatyzacja)
    return;
  }

  await WarehouseSchema.insert({
    orderId: order._id,
    sourceInstanceId: this.caller.instanceId,   // z którego sklepu
    sourceName: this.caller.instanceName        // np. 'Sklep PL'
  });
});
PoleTypOpis
this.callerobject | nullnull, gdy hook odpaliła platforma, a nie inny moduł
caller.instanceIdstringIdentyfikator instalacji, która odpaliła hook
caller.moduleCodestringKod modułu wywołującego, np. core/prestashop
caller.instanceNamestring | nullNazwa nadana instalacji przy instalacji

Tożsamość jest budowana przez platformę z tożsamości połączenia, więc wywołujący nie może jej podrobić.

Kontekst jest opcjonalny

Handler, który nie potrzebuje caller, zostaje funkcją strzałkową i niczego nie zmienia. Kontekstu potrzebujesz → napisz zwykłą function.

Uwaga: Function.prototype.bind ma pierwszeństwo przed wiązaniem robionym przez SDK – handler zarejestrowany jako obj.handler.bind(obj) (albo pole klasy ze strzałką) dostanie this.caller === undefined.

Odpowiadanie konkretnej instancji

Jeśli w reakcji na hook chcesz oddzwonić do jego nadawcy, użyj Core.callInstanceMethod(this.caller.instanceId, ...) – Core.callModule po samym kodzie modułu jest niejednoznaczne, gdy firma ma kilka instalacji. Szczegóły w Core.callModule.

Konwencja nazewnictwa hooków

Hooki są identyfikowane przez string w formacie:

domena.kontekst.akcja
SegmentOpisPrzykład
domenaModuł lub obszar systemu emitujący hookcore, shop, crm
kontekstObszar tematycznyshipping, payment, order
akcjaWykonywana operacjagetCarrier, beforeCreate, onComplete

Przykłady nazw hooków:

core.shipping.getCarrier     – pobranie dostępnych przewoźników
core.payment.getMethod       – pobranie dostępnych metod płatności
core.order.beforeCreate      – walidacja przed utworzeniem zamówienia
core.order.onComplete        – akcje po finalizacji zamówienia
shop.product.afterImport     – przetwarzanie po imporcie produktów

Hooki z prefiksem event: – przekazywanie na frontend

Hooki, których nazwa zaczyna się od event:, są objęte specjalną konwencją: platforma automatycznie przesyła je do podłączonych klientów frontendowych za pośrednictwem WebSocket. Dzięki temu komponenty UI mogą dynamicznie reagować na zmiany danych bez potrzeby odpytywania serwera.

await Core.execHook('event:order.update', { order });
await Core.execHook('event:product.delete', { productId });

Taki hook działa dwojako:

  • po stronie backendowej wywołuje wszystkie zarejestrowane handlery (jak każdy inny hook),
  • równocześnie rozsyła zdarzenie przez WebSocket do frontendów działających w kontekście danej firmy.

Przykład – aktualizacja zamówienia z powiadomieniem UI:

Core.registerMethod('order.update', async (params, ctx) => {
  const order = await OrdersSchema.findOneAndUpdate(
    { _id: params.orderId },
    { $set: params.data },
    { new: true }
  );

  // Hook z prefiksem event: zostanie automatycznie przekazany na frontend
  await Core.execHook('event:order.update', { order });

  return { success: true };
});

Kiedy używać prefiksu event:

Stosuj event: dla hooków reprezentujących zmiany stanu encji (aktualizacja, usunięcie, zmiana statusu), na które frontend powinien reagować w czasie rzeczywistym – np. odświeżenie listy zamówień, ukrycie usuniętego produktu czy aktualizacja licznika.

Dokumentuj hooki swojego modułu

Jeśli Twój moduł definiuje własne hooki (wywołuje execHook), opisz je w dokumentacji modułu razem z oczekiwanym formatem data i zwracanego wyniku. Umożliwi to innym deweloperom tworzenie integracji z Twoim modułem.

Pełny przykład integracji

Poniżej kompletny scenariusz: moduł core/orders finalizuje zamówienie i ogłasza hook, a moduł integracji z systemem magazynowym nasłuchuje na to zdarzenie.

Moduł core/orders (emituje hook):

Core.registerMethod('order.complete', async (params, ctx) => {
  const order = await OrdersSchema.findOne({ _id: params.orderId });

  await OrdersSchema.update(
    { _id: order._id },
    { $set: { status: 'completed', completedAt: new Date() } }
  );

  // Powiadom wszystkie zainteresowane moduły o finalizacji
  await Core.execHook('core.order.onComplete', { order });

  return { success: true };
});

Moduł magazynu (obsługuje hook):

Core.registerHook('core.order.onComplete', async ({ order }) => {
  Log.info('Aktualizacja stanu magazynowego po finalizacji', {
    orderId: order._id
  });

  for (const item of order.cart) {
    await WarehouseSchema.update(
      { sku: item.sku },
      { $inc: { stock: -item.quantity } }
    );
  }

  return { updated: order.cart.length };
});
Ostatnio zaktualizowano:: 29.07.2026, 09:25
Contributors: Wiktor Koźmiński
Prev
Core.callModule – bezpośrednie wywołania