Gdy aplikacja otrzymuje niejednoznaczne dane, wymuszenie odpowiedzi „prawidłowe” albo „nieprawidłowe” zazwyczaj ukrywa problem zamiast go rozwiązywać. Może się tak zdarzyć w przypadku formularzy, integracji lub wyników proponowanych przez system automatyczny: brakuje kontekstu, by bezpiecznie zaakceptować lub odrzucić rekord. W takich sytuacjach weryfikacja przez człowieka niepewnych danych w PHP powinna być stanem domenowym z jawnymi regułami, a nie nieformalną etykietą ani prowizoryczną kolejką.
Projekt musi odpowiadać na cztery pytania: co otrzymano, co jest proponowane, kto ma podjąć decyzję i co stanie się później. Rozdzielenie tych odpowiedzialności ułatwia obsługę procesu, poprawianie błędów i wyjaśnienie, dlaczego dane przetworzono w określony sposób.
Zaklasyfikuj problem, zanim utworzysz stany

Nie wszystkie dane, które nie mogą przejść dalej, wymagają weryfikacji przez człowieka. Rozróżnij co najmniej następujące sytuacje:
- Nieprawidłowe: nie spełniają weryfikowalnej reguły, na przykład mają format, którego nie można zinterpretować. Należy je odrzucić albo poprosić o korektę — zależnie od źródła i produktu.
- Niekompletne: brakuje wymaganego pola. Można poprosić o podanie danych, zachować wersję roboczą lub poczekać na dodatkowe źródło.
- Niepewne: możliwych jest kilka interpretacji albo dostępne dowody nie pozwalają podjąć automatycznej decyzji. Może być potrzebna ocena człowieka.
Weryfikacja nie powinna stać się domyślnym miejscem dla każdego błędu. Jeśli wartość narusza jednoznaczne ograniczenie, umieszczenie jej w kolejce weryfikacji tylko zwiększa nakład pracy. Gdy jednak decyzja zależy od kontekstu lub oceny, stan umożliwiający weryfikację pozwala wstrzymać przetwarzanie bez przedwczesnego uznawania danych za prawidłowe lub nieprawidłowe.
Warto też odróżnić ten proces od kwarantanny importu. Kwarantanna zwykle izoluje dane wejściowe, których nie należy włączać do systemu, na przykład dlatego, że nie przeszły kontroli technicznych lub bezpieczeństwa. Weryfikacja służy rozstrzygnięciu niejednoznaczności biznesowej. Ten sam rekord może przejść przez oba mechanizmy, ale ich cele, uprawnienia i kryteria zakończenia nie są zamienne.
Zdefiniuj niewielki i jawny cykl życia
Zacznij od stanów potrzebnych do opisania procesu, a nie od listy wszystkich możliwych przyszłych scenariuszy. Podstawowy cykl może wyglądać tak:
pending_review: oczekuje na decyzję.confirmed: osoba potwierdziła wartość, której można użyć.rejected: osoba uznała, że danych nie należy przetwarzać.reopenedlub nowy wpis w weryfikacji: wcześniejsza decyzja ponownie podlega ocenie.
Określ dozwolone przejścia. Na przykład sprawę oczekującą na decyzję można potwierdzić lub odrzucić; rozstrzygniętą sprawę można ponownie otworzyć wyłącznie w określonych, autoryzowanych warunkach. Nie pozwalaj, by dowolne żądanie bezpośrednio zmieniało stan na dowolną wartość. Przejście powinno określać stan początkowy i docelowy, dozwolonego aktora oraz powiązane skutki.
Zapisuj powód decyzji i tożsamość osoby odpowiedzialnej. Jeśli dane zostaną odrzucone, zapisz przydatne uzasadnienie, a nie tylko wartość logiczną. Jeśli interpretacja zostanie potwierdzona, zachowaj zatwierdzoną wartość i, w stosownych przypadkach, dowody, które uzasadniały decyzję. Określ, w przypadku których działań podanie powodu jest obowiązkowe, i unikaj umieszczania niepotrzebnych danych osobowych w dowolnych notatkach.
Oddziel dane wejściowe, propozycję i potwierdzoną wartość
Solidny projekt nie nadpisuje otrzymanych danych interpretacją procesu automatycznego ani korektą wprowadzoną przez człowieka. Zachowaj rozróżnienie między następującymi pojęciami:
- Otrzymane dane: oryginalne dane wejściowe i ich pochodzenie, z odpowiednimi kontrolami dostępu i zasadami przechowywania.
- Proponowana interpretacja: sugestia wygenerowana przez reguły, integrację lub system wspomagający, wraz z informacjami potrzebnymi do jej oceny.
- Potwierdzona wartość: wartość używana przez aplikację po podjęciu autoryzowanej decyzji.
Ten podział zapobiega przypadkowemu potraktowaniu propozycji jako prawdy i umożliwia odtworzenie sposobu, w jaki ustalono bieżącą wartość. Nie trzeba bezrefleksyjnie duplikować każdego pola: zachowuj dane potrzebne do audytu i wsparcia, stosując zasady retencji i ochrony danych. Jeśli propozycja zawiera ocenę punktową lub automatycznie wygenerowane wyjaśnienie, traktuj je jako kontekst, a nie decyzję.
Zapisuj zmiany jako zdarzenia lub niezmienne wpisy historii, których zwykłe działanie aplikacji nie modyfikuje: identyfikator sprawy, działanie, poprzedni i nowy stan, aktora, datę oraz powód. Model może dodatkowo przechowywać bieżący stan, aby ułatwić przeglądanie kolejki. Korektę przedstaw jako nową decyzję lub ponowne otwarcie powiązane z historią; usunięcie poprzedniej decyzji niszczy możliwość prześledzenia zmian.
Implementuj decyzje w warstwie domenowej
W PHP zamknij logikę przejść w serwisie aplikacyjnym lub encji domenowej, zamiast polegać na tym, że każdy kontroler zastosuje te same reguły. Przykład schematyczny:
final class ReviewDecisionService
{
public function confirm(ReviewCase $case, User $actor, string $value, string $reason): void
{
if (!$actor->canReview($case)) {
throw new DomainException('Brak uprawnień');
}
if (!$case->isPending()) {
throw new DomainException('Sprawa nie oczekuje na decyzję');
}
$case->confirm($value, $actor->id(), $reason);
}
}
Ten fragment pokazuje, gdzie umieścić reguły, a nie kompletną implementację. Sprawdź także format i ograniczenia wartości, upewnij się, że aktor jest uprawniony do rozstrzygnięcia danej sprawy, i zapisz decyzję. Nie myl uwierzytelniania — ustalania, kim jest użytkownik — z autoryzacją, czyli określaniem, co może zrobić w odniesieniu do danego rekordu. Jeśli obowiązują role lub rozdział obowiązków, wyraź te ograniczenia jako reguły produktu.
Jeśli aktualizacja sprawy i zapis historii muszą być spójne, wykonaj je w jednej transakcji bazodanowej. Gdy decyzja wywołuje skutki zewnętrzne, takie jak wywołanie innego API, nie zakładaj, że lokalna transakcja obejmuje również ten system. Korzystaj z niezawodnego mechanizmu dostarczania, takiego jak transakcyjny mechanizm outbox, gdy wymaga tego architektura, i projektuj konsumentów tak, aby tolerowali ponowienia.
Zaprojektuj kolejkę, która ułatwia podejmowanie decyzji
Użyteczna kolejka pozwala ustalać priorytety i wyszukiwać sprawy, nie ukrywając ich kontekstu. Uwzględnij odpowiednie filtry — stan, datę, typ lub przypisanie — i pokazuj tylko informacje potrzebne do zrozumienia sprawy. Jeśli istnieje propozycja, wyraźnie zaznacz, że to tylko propozycja. Pokaż pochodzenie, powód weryfikacji i dostępne działania, a także unikaj wyświetlania kontrolek, których użytkownik nie ma uprawnień używać.
Przypisywanie spraw może ograniczyć powielanie pracy, ale nie zastępuje autoryzacji ani ochrony przed równoczesnymi decyzjami. Jedna osoba może otworzyć sprawę, a druga rozstrzygnąć ją, zanim pierwsza wyśle odpowiedź. Aby stary widok nie nadpisał nowszej zmiany, stosuj optymistyczną kontrolę współbieżności: podczas zapisu porównuj wersję lub znacznik czasu i odrzucaj nieaktualną operację, aby użytkownik ponownie załadował sprawę.
Jeśli proces wymaga tymczasowych rezerwacji, określ ich czas wygaśnięcia i zachowanie po upływie tego terminu. Nie używaj trwałego przypisania jako przypadkowej blokady. Rejestruj istotne działania, aby można było ustalić, kto zweryfikował sprawę, i wykrywać rekordy, które pozostają nierozstrzygnięte dłużej, niż dopuszcza to działalność operacyjna.
Koryguj i wznawiaj sprawy bez usuwania historii
Decyzja może być błędna lub wymagać zmiany po pojawieniu się nowych dowodów. Określ, kto może ponownie otworzyć sprawę, jaki powód musi podać i czy potwierdzona wartość przestaje być dostępna na czas ponownej weryfikacji. Odpowiedź zależy od wpływu tej wartości na dalsze procesy; może być na przykład konieczne wstrzymanie nowych działań, ale nie automatyczne usuwanie skutków, które już wystąpiły.
Zachowaj poprzednią decyzję w historii i utwórz nową wersję lub zdarzenie weryfikacji. Jeśli korekta danych wymaga ponownego przeliczenia powiązanych informacji, wskaż te skutki jawnie i określ idempotentną procedurę, aby uniknąć duplikatów. Korekta stanu nie może sugerować, że operacja zewnętrzna została już cofnięta.
Testuj reguły, uprawnienia i dalsze skutki

Testy powinny obejmować cały proces i jego ograniczenia, a nie tylko zapis formularza. Co najmniej sprawdź, czy:
- sprawa oczekująca na decyzję dopuszcza przewidziane przejścia i odrzuca pozostałe;
- aktorzy bez odpowiednich uprawnień nie mogą podejmować decyzji, ponownie otwierać spraw ani wyświetlać zastrzeżonych danych;
- każda decyzja zachowuje aktora, powód oraz poprzednie i nowe wartości zgodnie z przyjętą polityką;
- dwie równoczesne zmiany nie pozwalają, by nieaktualna decyzja nadpisała inną;
- błąd podczas zapisu historii nie pozostawia zaktualizowanego stanu bez śladu audytowego;
- ponowienie działania następczego nie duplikuje operacji ani powiadomień.
Jako listę kontrolną projektu wykorzystaj następujące punkty: niepewność ma możliwą do zidentyfikowania przyczynę; oryginalne dane, propozycja i potwierdzona wartość są rozdzielone; każde przejście ma przypisane uprawnienie i powód; kolejka zapewnia wystarczający kontekst; a ponowne otwarcie zachowuje historię i określa wpływ operacyjny. Jeśli którykolwiek z tych punktów zależy od nieformalnych ustaleń, w systemie wciąż brakuje odpowiedniej reguły.



