Kontekst firmy i instancji
Orbito to platforma wielofirmowa (multi-tenant) – na jednej instancji może działać wiele niezależnych firm. Twój moduł nie musi jednak zarządzać tym kontekstem samodzielnie: platforma izoluje firmy na poziomie infrastruktury.
Jeden kontener = jedna instancja modułu = jedna firma
Każda instalacja modułu działa jako osobny kontener, który obsługuje wyłącznie tę jedną instalację jednej firmy. Kontener uwierzytelnia się tokenem przekazanym przy starcie, a Orbito na podstawie tego tokenu ustala tożsamość połączenia.
Firma może zainstalować ten sam moduł wiele razy – np. trzy sklepy Prestashop albo dwa konta kurierskie. Każda taka instalacja to osobna instancja (instanceId) z własnym kontenerem, własną konfiguracją i własnymi danymi. Instancje tego samego modułu nie widzą nawzajem swoich kolekcji ani ustawień.
Moduł a instancja
- moduł (
moduleCode, np.core/prestashop) – co zostało zainstalowane, - instancja (
instanceId) – konkretna instalacja modułu u konkretnej firmy.
Adresowanie danych, blokad i konfiguracji odbywa się po instanceId.
Tożsamość modułu
Modułu nie interesuje, skąd bierze się jego tożsamość – odczytuje ją z platformy:
const { Core } = require('@orbito/api-lib');
Core.getCompanyId(); // firma obsługiwana przez ten kontener
Core.getInstanceId(); // identyfikator TEJ instalacji modułu
Core.getModuleInfo(); // { instanceId, companyId, code, version }
Obie wartości są stałe przez cały cykl życia procesu i pochodzą wyłącznie z tokenu połączenia. Moduł nigdy ich nie wysyła i nie może ich podmienić.
Dane firmy
Core.getCompanyId() zwraca tylko identyfikator. Pełne dane firmy (nazwa, NIP, adres, strefa czasowa) moduł pobiera przez Core.getCompany() – patrz Core.getCompany.
Handlery metod
Handler zarejestrowanej metody otrzymuje wyłącznie params i ctx:
const { Core, Log } = require('@orbito/api-lib');
Core.registerMethod('search', async (params, ctx) => {
Log.info('Wywołano search', { params });
// Operujesz automatycznie na danych swojej firmy
const results = await MySchema.find(params);
return results;
});
Parametry handlera
| Parametr | Typ | Opis |
|---|---|---|
params | object | Argumenty przekazane przez wywołującego |
ctx | object | Kontekst żądania – dane sesji, uprawnienia, ctx.caller |
Hooki
Handlery hooków również nie otrzymują companyId – dostają wyłącznie argumenty przekazane przez wywołującego:
Core.registerHook('core.shipping.getCarrier', async (order) => {
const config = await ShippingConfig.findOne({});
return config?.carrier ?? null;
});
Kto mnie wywołał – caller
Przy ruchu moduł → moduł (wywołania metod i hooki) platforma dokłada tożsamość wywołującego:
{
instanceId, // identyfikator instancji, która wywołała
moduleCode, // kod modułu, np. 'core/prestashop'
instanceName // nazwa nadana przy instalacji (np. 'Sklep PL'), może być null
}
Jest ona budowana przez platformę z tożsamości połączenia – wywołujący nie może jej podrobić. Gdy wywołanie pochodzi od samej platformy (akcja użytkownika, automatyzacja), caller jest null.
W handlerze metody dostajesz ją w ctx.caller, a w handlerze hooka jako this.caller – hook nie dostaje kontekstu jako argumentu, bo liczba argumentów hooka należy do wywołującego:
// nie potrzebuję kontekstu – funkcja strzałkowa
Core.registerHook('event:order:created', async (order) => {
await saveOrder(order);
});
// potrzebuję kontekstu – zwykła function
Core.registerHook('event:order:created', async function (order) {
await saveOrder(order, { sourceInstanceId: this.caller?.instanceId });
});
Dzięki temu moduł magazynowy odbierający zdarzenia z trzech sklepów wie, z którego sklepu przyszło zamówienie, bez zgadywania po treści danych.
bind() wygrywa z kontekstem
Jeśli zarejestrujesz handler jako obj.handler.bind(obj) albo jako metodę-pole ze strzałką, this.caller będzie undefined – wiązanie bind ma pierwszeństwo. Potrzebujesz kontekstu → rejestruj zwykłą function.
Izolacja danych
Orbito automatycznie nadaje kolekcjom namespace per instancja modułu – moduł posługuje się surową, logiczną nazwą kolekcji (np. orders), a platforma tłumaczy ją na fizyczną kolekcję danej instalacji. Dwie instancje tego samego modułu w jednej firmie mają rozłączne dane.
Dlatego nie dodawaj pola companyId do schematów ani do warunków zapytań:
const { DB } = require('@orbito/api-lib');
const OrdersSchema = new DB.Schema('orders', {
orderNumber: { type: 'string' },
// ...
});
// ✅ Dobrze – zwróci wyłącznie zamówienia Twojej firmy
const orders = await OrdersSchema.find({});
// ❌ Źle – companyId nie jest już polem dokumentu, filtr nic nie znajdzie
const orders = await OrdersSchema.find({ companyId });
Migracja ze starszej wersji SDK
Jeśli Twój moduł powstał przed wprowadzeniem pełnej izolacji, usuń z niego:
- argumenty
companyId/instanceIdz handlerów metod, hooków i stron, companyIdz wywołańCore.callModule,Core.execHookiLog.*,- pole
companyIdze schematów, filtrów zapytań i indeksów, - ręczne prefiksowanie nazw kolekcji i identyfikatorów blokad.
Dodatkowo od SDK 3.0.0 (multi-instancja):
- handlery hooków dostają wyłącznie argumenty wywołującego – jeśli Twój handler przyjmował kontekst jako pierwszy lub ostatni argument, przenieś odczyt na
this.caller(i zamień funkcję strzałkową nafunction), Core.callModulemoże rzucić błądAMBIGUOUS_MODULE_TARGET, gdy firma ma kilka instalacji modułu docelowego – wtedy adresuj poinstanceIdprzezCore.callInstanceMethod,- w automatyzacjach użytkownik wybiera instancję parametrem
__instanceIdwstrzykiwanym przez platformę – nazwa jest zarezerwowana, a Twoje akcje i triggery jej nie widzą (patrz Automation).
Indeksy w MongoDB
Ponieważ każda instancja ma własną kolekcję, indeksuj wyłącznie pola, po których faktycznie wyszukujesz – bez companyId:
DB.createIndex('orders', { orderNumber: 1 }, { unique: true });
