Mendix Entity Events: Before Commit czy After Commit? Praktyczne podejście architektoniczne

Entity Events w Mendix pozwalają uruchamiać logikę automatycznie w określonych momentach cyklu życia obiektu. Na pierwszy rzut oka wybór odpowiedniego eventu wydaje się prosty: przed utworzeniem ustawiamy wartości, po utworzeniu wykonujemy dodatkową logikę, przed usunięciem sprawdzamy warunki, a po commicie uruchamiamy działania następcze.
W praktycznych projektach sprawa nie zawsze jest jednak tak oczywista.
Moment uruchomienia logiki jest decyzją architektoniczną. Znaczenie ma nie tylko to, co chcemy zrobić, ale również to, czy obiekt istnieje już w bazie danych, czy operacja może zostać zablokowana, czy dana logika powinna wykonać się tylko przy tworzeniu obiektu oraz czy wykonywana operacja zależy od systemu zewnętrznego.
W tym artykule uporządkujemy dostępne Entity Events, a następnie przyjrzymy się praktycznemu scenariuszowi, w którym świadomie wybrano Before Commit zamiast rozwiązania, które na pierwszy rzut oka mogłoby wydawać się bardziej naturalne.
Entity Events — gdzie dokładnie wykonuje się logika?
Entity Events są powiązane z cyklem życia obiektu. W zależności od wybranego eventu logika może zostać wykonana:
- przed utworzeniem obiektu,
- po utworzeniu obiektu,
- przed jego usunięciem,
- po jego usunięciu,
- przed zapisaniem zmian do bazy danych,
- po zapisaniu zmian do bazy danych.
Podstawowe różnice przedstawia poniższa tabela:
| Event | Moment wykonania | Typowe zastosowanie | Możliwość zablokowania operacji |
|---|---|---|---|
| Before Create | przed utworzeniem obiektu w pamięci | wartości domyślne, inicjalizacja danych | tak |
| After Create | po utworzeniu obiektu w pamięci, przed commitem | dodatkowa logika związana z utworzeniem | nie |
| Before Delete | przed usunięciem obiektu | walidacja, obsługa danych zależnych, archiwizacja | tak |
| After Delete | po usunięciu obiektu | czyszczenie danych, logowanie, powiadomienia | nie |
| Before Commit | przed zapisaniem obiektu do bazy | walidacja, transformacja danych, kontrola zapisu | tak |
| After Commit | po zapisaniu obiektu do bazy | działania następcze, logowanie, aktualizacja danych powiązanych | nie |
Samo zestawienie eventów nie odpowiada jednak na najważniejsze pytanie:
Który event powinienem wybrać dla konkretnej logiki biznesowej?
Żeby odpowiedzieć na to pytanie, trzeba spojrzeć nie tylko na nazwę eventu, ale przede wszystkim na moment, w którym dana operacja powinna się wydarzyć.
1. Before Create — zanim obiekt powstanie
Before Create uruchamia się tuż przed utworzeniem nowego obiektu w pamięci.
To dobre miejsce na logikę, która ma przygotować obiekt jeszcze zanim zostanie utworzony.
Typowe zastosowania to:
- ustawianie wartości domyślnych,
- inicjalizacja danych,
- przygotowanie powiązań,
- sprawdzenie warunków, które muszą zostać spełnione, aby obiekt mógł zostać utworzony.
Przykładowo, w aplikacji do rejestrowania czasu pracy Before Create dla encji Pracownik może ustawić początkową wartość wynagrodzenia albo przygotować unikalny identyfikator.
Kluczowa cecha tego eventu jest prosta:
pracujemy jeszcze na etapie tworzenia obiektu.
Jeżeli logika wymaga danych, które pojawiają się dopiero później w cyklu życia obiektu, Before Create nie będzie odpowiednim miejscem.
2. After Create — obiekt już istnieje, ale jeszcze nie został zapisany
After Create wykonywany jest po utworzeniu obiektu w pamięci, ale przed jego zatwierdzeniem w bazie danych.
Daje to możliwość wykonania logiki wykorzystującej już utworzony obiekt.
Możemy tutaj między innymi:
- ustawić wartości zależne od innych danych,
- utworzyć lub przygotować powiązane dane,
- uruchomić dodatkową logikę,
- zarejestrować fakt utworzenia obiektu.
Przykładowo, w aplikacji do zarządzania projektami After Create dla encji Zadanie może sprawdzić status powiązanego projektu i ustawić zadanie jako aktywne, jeśli projekt jest aktywny.
Warto jednak pamiętać o jednej rzeczy:
After Create nie oznacza jeszcze, że obiekt został zapisany w bazie danych.
To rozróżnienie staje się szczególnie ważne, gdy logika zaczyna wychodzić poza samą aplikację.
3. Before Delete — ostatni moment przed usunięciem
Before Delete uruchamia się przed usunięciem obiektu z bazy danych.
To naturalne miejsce dla logiki, która ma zdecydować, czy usunięcie w ogóle powinno nastąpić.
Możemy tutaj między innymi:
- sprawdzić warunki pozwalające na usunięcie,
- obsłużyć dane zależne,
- zarchiwizować informacje,
- wykonać dodatkowe operacje przed usunięciem.
Przykładem może być system CRM, w którym nie chcemy pozwolić na usunięcie klienta posiadającego aktywne zamówienia.
Before Delete może sprawdzić ten warunek i zablokować operację.
To istotna różnica względem After Delete: w przypadku Before Delete obiekt nadal istnieje, więc można jeszcze zdecydować, czy operacja ma dojść do skutku.
4. After Delete — logika po usunięciu
After Delete wykonuje się po usunięciu obiektu.
Jego zastosowanie jest więc inne niż Before Delete.
Nie służy już do decydowania, czy obiekt można usunąć. Jest przeznaczony raczej do działań następczych, takich jak:
- aktualizacja powiązanych danych,
- czyszczenie danych,
- rejestrowanie informacji o usunięciu,
- wysyłanie powiadomień,
- wykonanie dodatkowych działań w systemach zewnętrznych.
Przykładowo, w systemie zarządzania zapasami After Delete może usunąć produkt z indeksu wyszukiwania albo uruchomić powiadomienie związane z jego usunięciem.
Podstawowa zasada jest tutaj prosta:
Jeżeli potrzebujemy jeszcze zdecydować, czy obiekt może zostać usunięty — Before Delete. Jeżeli reagujemy na fakt, że został usunięty — After Delete.
5. Before Commit — ostatni moment na zmianę danych
Before Commit uruchamia się tuż przed zapisaniem obiektu do bazy danych.
To szczególnie istotny event, ponieważ możemy jeszcze:
- zweryfikować dane,
- zmodyfikować wartości,
- wykonać transformację,
- zablokować zapis, jeśli warunki nie zostały spełnione.
Przykładowo, w aplikacji e-commerce Before Commit dla encji Zamówienie może sprawdzić, czy wartość zamówienia jest większa od zera.
Jeżeli walidacja zakończy się błędem, zapis może zostać zablokowany.
Before Commit jest więc naturalnym miejscem dla logiki, której wykonanie powinno mieć bezpośredni związek z zapisem danych.
Ale właśnie tutaj pojawia się ciekawsze pytanie:
Czy każda logika wykonywana w związku z utworzeniem rekordu powinna zostać przeniesiona do After Commit?
Nie zawsze.
6. After Commit — kiedy dane zostały już zapisane
After Commit wykonywany jest po zatwierdzeniu obiektu w bazie danych.
To dobry moment na operacje, które powinny nastąpić dopiero po skutecznym zapisaniu danych.
Możemy tutaj między innymi:
- uruchamiać procesy następcze,
- tworzyć wpisy audytowe,
- rejestrować zdarzenia,
- aktualizować powiązane dane,
- uruchamiać powiadomienia.
Przykładowo, po utworzeniu zamówienia można uruchomić powiadomienie dla przedstawiciela handlowego.
Problem zaczyna się wtedy, gdy powiadomienie nie jest prostą operacją wykonywaną bezpośrednio przez aplikację, ale częścią większego, asynchronicznego procesu.
I właśnie taki scenariusz warto przeanalizować.
7. Case study — kiedy Before Commit ma więcej sensu niż After Commit?
Załóżmy system, w którym po utworzeniu wezwania należy wysłać powiadomienie SMS oraz email.
Na pierwszy rzut oka naturalnym rozwiązaniem wydaje się użycie After Create albo After Commit.
W końcu chcemy wysłać powiadomienie po utworzeniu wezwania.
Jednak w tym przypadku sama informacja o utworzeniu rekordu nie oznacza jeszcze, że powiadomienie zostało faktycznie wysłane.
Jak wygląda architektura?
Wysyłka powiadomień odbywa się poprzez niezależną szynę danych, która przetwarza komunikaty cyklicznie.
Oznacza to, że aplikacja w momencie utworzenia wezwania nie jest w stanie stwierdzić, czy SMS lub email został już skutecznie wysłany.
To ma istotną konsekwencję.
Nie możemy potraktować samego uruchomienia logiki wysyłającej jako dowodu, że operacja zewnętrzna zakończyła się sukcesem.
Schemat można przedstawić następująco:
Aplikacja
│
│ utworzenie wezwania
▼
Entity Event
│
│ przekazanie informacji
▼
Szyna danych
│
│ przetwarzanie asynchroniczne
▼
SMS / Email
Między zapisaniem rekordu a faktycznym wykonaniem operacji istnieje więc dodatkowa warstwa.
8. Dlaczego w takim przypadku można wykorzystać Before Commit?
W analizowanym scenariuszu logika została umieszczona w Before Commit, a nie w After Create.
Decyzja wynikała z dwóch podstawowych wymagań.
Po pierwsze — zabezpieczenie przed ponownym wysłaniem
Powiadomienie powinno zostać wysłane tylko w momencie tworzenia rekordu.
Jeżeli wezwanie zostanie później edytowane, powiadomienie nie powinno zostać wysłane ponownie.
Musimy więc rozróżnić:
NOWY OBIEKT
│
└──► wyślij powiadomienie
ISTNIEJĄCY OBIEKT
│
└──► nie wysyłaj ponownie
Dlatego przed wykonaniem logiki sprawdzamy, czy obiekt jest nowy, czy już istnieje w bazie.
Taką kontrolę można wykonać przed commitem.
Po drugie — charakter integracji
Sama aplikacja nie ma w momencie utworzenia wezwania pełnej wiedzy o wyniku wysyłki realizowanej przez szynę danych.
Nie możemy więc traktować odpowiedzi aplikacji jako potwierdzenia, że SMS lub email rzeczywiście został wysłany.
To właśnie architektura integracji, a nie sama nazwa operacji, wpływa na wybór odpowiedniego eventu.
9. Zapis rekordu a wykonanie operacji zewnętrznej to dwie różne rzeczy
To jedna z ważniejszych lekcji płynących z tego przykładu.
W systemach zintegrowanych często mamy do czynienia z kilkoma niezależnymi zdarzeniami:
- aplikacja zmienia dane,
- dane zostają zapisane,
- informacja trafia do systemu pośredniczącego,
- system zewnętrzny odbiera komunikat,
- operacja zostaje wykonana,
- system może zwrócić informację o wyniku.
Nie należy automatycznie traktować tych wszystkich kroków jako jednej operacji.
Dlatego wybór pomiędzy Before Commit a After Commit powinien uwzględniać nie tylko bazę danych, ale również sposób działania całej integracji.
10. „Wykonać raz” nie zawsze oznacza „wykonać tylko raz”
W przypadku powiadomień szczególnie ważne jest zabezpieczenie przed wielokrotnym wykonaniem logiki.
Jeżeli powiadomienie ma być wysłane tylko przy utworzeniu rekordu, sama obecność Entity Event nie daje automatycznie gwarancji, że logika nie zostanie uruchomiona ponownie przy późniejszej edycji.
Dlatego warto świadomie zabezpieczać takie scenariusze.
Jednym z prostych mechanizmów jest sprawdzenie, czy obiekt jest nowy.
Dopiero wtedy uruchamiana jest logika związana z wysłaniem powiadomienia.
ąMoże to wyglądać następująco:
┌─────────────────┐
│ Entity Commit │
└────────┬────────┘
│
▼
Czy obiekt nowy?
/ \
TAK NIE
│ │
▼ ▼
Powiadomienie Brak akcji
To może wydawać się drobnym szczegółem implementacyjnym, ale z punktu widzenia systemu jest bardzo istotne.
Duplikat SMS-a lub emaila nie jest wyłącznie problemem technicznym. Może stać się problemem biznesowym.
11. Jak wybierać Entity Event?
W praktyce warto zacząć nie od nazwy eventu, ale od kilku prostych pytań.
Czy logika ma przygotować obiekt?
→ rozważ Before Create.
Czy potrzebuję już utworzonego obiektu, ale niekoniecznie zapisanego?
→ rozważ After Create.
Czy muszę zweryfikować dane przed zapisem?
→ rozważ Before Commit.
Czy operacja może zostać wykonana dopiero po skutecznym zapisie?
→ rozważ After Commit.
Czy muszę zdecydować, czy obiekt może zostać usunięty?
→ Before Delete.
Czy reaguję na fakt, że obiekt został już usunięty?
→ After Delete.
To jednak dopiero pierwszy poziom analizy.
Jeżeli event uruchamia integrację z systemem zewnętrznym, trzeba dodatkowo odpowiedzieć na pytania:
- Czy operacja jest synchroniczna czy asynchroniczna?
- Czy aplikacja zna wynik operacji?
- Co się stanie, jeżeli operacja zostanie wykonana ponownie?
- Czy możliwe jest wystąpienie duplikatu?
- Co się stanie w przypadku błędu?
- Czy operacja zewnętrzna powinna być związana z transakcją bazy danych?
Dopiero odpowiedzi na te pytania pozwalają świadomie wybrać miejsce dla logiki.
12. Praktyczne zasady pracy z Entity Events
1. Nie wybieraj eventu tylko na podstawie jego nazwy
„Po utworzeniu” nie zawsze oznacza, że najlepszym wyborem będzie After Create.
Najpierw określ, kiedy operacja rzeczywiście powinna zostać wykonana.
2. Rozróżniaj obiekt w pamięci od rekordu zapisanego w bazie
After Create i After Commit reprezentują dwa różne momenty cyklu życia obiektu.
3. Logikę walidującą zapisuj przed commitem
Jeżeli dana reguła ma decydować o tym, czy dane mogą zostać zapisane, Before Commit jest naturalnym miejscem na jej realizację.
4. Nie traktuj operacji zewnętrznej jako części zwykłego zapisu
Integracja z SMS, email, API czy innym systemem może mieć zupełnie inny cykl życia niż transakcja w bazie danych.
5. Myśl o ponownym wykonaniu
Jeżeli dana operacja nie może zostać wykonana drugi raz, zabezpiecz to explicite.
6. Event powinien mieć jasno określoną odpowiedzialność
Im więcej logiki trafia do eventów, tym łatwiej stracić kontrolę nad tym, co właściwie dzieje się podczas zapisu encji.
Podsumowanie
Entity Events w Mendix pozwalają reagować na kolejne etapy cyklu życia obiektu. Same mechanizmy są stosunkowo proste.
Trudniejsza jest odpowiedź na pytanie:
W którym momencie powinna zostać wykonana konkretna logika biznesowa?
Nie zawsze można odpowiedzieć na nie prostą regułą.
W przypadku integracji z systemami zewnętrznymi szczególnie ważne jest rozróżnienie pomiędzy zapisaniem danych a faktycznym wykonaniem operacji.
After Commit może być właściwym wyborem, jeżeli dana operacja rzeczywiście powinna rozpocząć się dopiero po skutecznym zapisaniu danych.
Before Commit może być natomiast lepszym rozwiązaniem, gdy logika musi zostać wykonana w ramach procesu prowadzącego do zapisu i jednocześnie potrzebujemy kontroli nad tym, czy operacja dotyczy nowego obiektu.
Najważniejsza zasada brzmi:
Entity Event nie powinien być wybierany tylko dlatego, że jego nazwa pasuje do operacji. Powinien wynikać z wymagań dotyczących cyklu życia danych, transakcji, integracji i sposobu wykonania logiki.
To właśnie tutaj low-code przestaje być tylko kwestią konfiguracji platformy.
Zaczyna być kwestią architektury.