19.05.2026
Podstawy

Apex w Salesforce: architektura języka i governor limits

Apex w Salesforce: architektura języka i governor limits

Apex to obiektowy język programowania Salesforce działający wyłącznie po stronie serwera – nie w przeglądarce – i ściśle kontrolowany przez mechanizm governor limits (oficjalna dokumentacja Salesforce Developer). Limity te istnieją dlatego, że Apex działa w środowisku multi-tenant: jeden serwer obsługuje równocześnie wiele organizacji, a kod jednej z nich nie może monopolizować wspólnych zasobów. Dla dewelopera oznacza to jedno: Apex pisany bez świadomości limitów działa na małych zestawach danych i pada spektakularnie na produkcji. Poniżej znajdziesz to, co faktycznie musisz wiedzieć zanim napiszesz pierwszy trigger lub klasę Apex w poważnym projekcie.

Czym jest Apex i jak różni się od zwykłego Javy

Apex jest językiem silnie typowanym, obiektowym, składniowo podobnym do Javy – do tego stopnia, że deweloperzy z backgroundem Java lub C# zazwyczaj piszą pierwsze klasy Apex w kilka godzin. Zasadnicze różnice to środowisko wykonania i zakres możliwości. Apex działa tylko na serwerach Salesforce, nie ma dostępu do systemu plików, nie może tworzyć wątków ani korzystać z zewnętrznych bibliotek spoza Salesforce. Kompensuje to integracja z platformą: SOQL i DML są częścią składni języka, nie bibliotekami zewnętrznymi.

Apex można pisać i uruchamiać na kilka sposobów. Developer Console to interfejs przeglądarkowy dostępny bezpośrednio w orgu – dobry do szybkich eksperymentów i Execute Anonymous. Visual Studio Code z rozszerzeniem Salesforce Extension Pack to środowisko zalecane do poważniejszego development – kod trafia do systemu kontroli wersji, a deployment odbywa się przez Salesforce CLI lub Metadata API. Klasy i triggery można też tworzyć przez Setup → Apex Classes lub Triggers, ale to rozwiązanie tylko dla bardzo małych zmian.

Kod Apex wykonuje się w kontekście transakcji. Pojedyncza transakcja to zestaw operacji uruchamiany przez jedno zdarzenie – np. zapisanie rekordu, wywołanie przez Flow lub żądanie API. Transakcja albo kończy się powodzeniem (wszystko zapisane), albo jest wycofywana w całości (rollback). Nie ma czegoś takiego jak „częściowe zapisanie” w ramach jednej transakcji – to fundamentalna różnica od baz danych, z którymi wcześniej pracowali programiści SQL.

Governor limits: liczby, które kształtują architekturę

Governor limits są egzekwowane w czasie wykonania kodu. Przekroczenie limitu rzuca wyjątkiem, którego nie można obsłużyć – transakcja pada. Najważniejsze limity per transakcja synchroniczna (oficjalna dokumentacja Salesforce):

SOQL queries: 100 (synchronicznie), 200 (asynchronicznie). DML statements: 150. Rekordy przetworzone przez DML: 10 000. CPU time: 10 000 ms synchronicznie, 60 000 ms asynchronicznie. Heap size: 6 MB synchronicznie, 12 MB asynchronicznie.

Dwa limity, na które trafiają najczęściej programiści z mniejszym doświadczeniem, to SOQL query limit i DML statement limit – zwłaszcza w źle napisanych triggerach. Klasyczny błąd: query lub DML wewnątrz pętli iterującej po rekordach. Przy jednym rekordzie – działa. Przy stu – 100 SOQL w jednej transakcji. Przy 101 – wyjątek. Na produkcji zazwyczaj odkrywany dopiero gdy import danych lub masowa aktualizacja uruchamia trigger na setkach rekordów jednocześnie.

Poprawny wzorzec: wszystkie ID zbierasz przed pętlą, wykonujesz jedno query do Mapy, w pętli tylko czytasz z Mapy. Jeden SOQL zamiast N SOQL, gdzie N = liczba rekordów w transakcji. Analogicznie z DML: zbierasz rekordy do listy, jeden insert lub update po pętli.

Wzorce triggerów: one trigger per object i handler class

Trigger to specjalna klasa Apex reagująca na zdarzenia DML – before insert, after insert, before update, after update, before delete, after delete, after undelete. „Before” oznacza przed zapisem do bazy, „after” – po zapisie, gdy rekord ma już przypisane ID.

Wzorzec zalecany przez społeczność i Salesforce: jeden trigger per obiekt, całą logikę w osobnej klasie handlera. Trigger ma tylko dwa zadania: zidentyfikować zdarzenie i delegować do handlera. Dlaczego? Wiele triggerów na tym samym obiekcie wykonuje się w nieokreślonej kolejności – nie ma gwarancji, który uruchomi się pierwszy. Jeden trigger z logiką w handlerze daje kontrolę nad kolejnością wykonania i ułatwia testy jednostkowe.

Przy pisaniu triggerów obowiązuje zasada bulkification: kod musi działać poprawnie niezależnie od tego, czy transakcja dotyczy jednego rekordu czy 200 (domyślny limit Bulk API). Trigger.new i Trigger.old to zawsze kolekcje – iterujesz po nich bez querowania wewnątrz pętli. Wyjątek od tej reguły nie istnieje.

Asynchroniczny Apex: kiedy wykraczasz poza synchroniczne limity

Gdy logika musi przetwarzać więcej danych niż pozwalają synchroniczne limity, lub gdy potrzebujesz callout do zewnętrznego systemu z triggera, wchodzi asynchroniczny Apex. Cztery warianty:

Future methods – metody oznaczone @future, wykonywane po zakończeniu bieżącej transakcji. Najprostsze. Ograniczenia: nie przyjmują obiektów Salesforce jako argumentów (tylko typy prymitywne), nie można ich wołać z innej metody @future.

Queueable Apex – bardziej elastyczna alternatywa dla @future. Klasa implementuje interfejs Queueable, można przekazywać obiekty, można chainować kolejne Queueable z wnętrza klasy. Standard dla większości asynchronicznych operacji w nowoczesnym kodzie Salesforce.

Batch Apex (Batch Apex, czyli asynchroniczny mechanizm przetwarzania dużych zbiorów danych) – klasa implementuje Database.Batchable. Salesforce dzieli rekordy na porcje (domyślnie 200, max 2000), każda porcja to osobna transakcja z własnym zestawem limitów. Jeśli musisz przetworzyć miliony rekordów, to jedyne właściwe narzędzie. Nie używaj Batch Apex do operacji, gdzie kolejność rekordów ma znaczenie – kolejność wykonania partii nie jest gwarantowana.

Scheduled Apex – klasa implementuje Schedulable, uruchamiana przez scheduler Salesforce według wyrażenia CRON. Najczęściej używana do uruchamiania Batch Apex o określonych porach.

Asynchroniczny Apex ma własne limity: maksymalnie 5 równoległych instancji Batch Apex, 100 zadań Queueable w kolejce na raz, i wliczanie do dziennego limitu asynchronicznych transakcji całego orga. Jeśli planujesz certyfikat Salesforce Platform Developer I, governor limits i wzorce asynchroniczne to obszary, na których skupia się egzamin – piszemy o tym w osobnym przewodniku po certyfikacie PD1. Do ćwiczenia tych konceptów przy przygotowaniu do certyfikacji warto też użyć ApexGoat.pro – narzędzia z pytaniami linkowanymi do oficjalnej dokumentacji.

Apex jest fundamentem całej programowalnej warstwy Salesforce – bez niego nie da się wyjść poza to, co Flow i konfiguracja deklaratywna oferują. Kluczem do sprawnego kodu nie jest znajomość składni, ale rozumienie transakcji, limitów i tego, dlaczego platforma działa tak a nie inaczej. Które z wymienionych tu wzorców stosujesz w swoich projektach?