Systemy informatyczne są bardzo różne, zarówno pod kątem dziedziny, której dotyczą, platform, na których działają, jak i odbiorców, do których są kierowane. Jeśli już mielibyśmy znaleźć jakąś część wspólną, to byłoby wykonywanie podstawowych operacji dodawania, odczytywania itd. na obiektach biznesowych, co lepiej znamy pod skrótem CRUD. Dzisiaj jednak nie będzie nas interesować tworzenie, wyświetlanie czy aktualizacja. Zamiast tego skupimy się na tym, co kryje się pod "D" w tym terminie, a więc delete - usuwaniu.
Każdy oczekuje, że jeśli cokolwiek możemy dodać w aplikacji, to powinniśmy to też móc usunąć. Powody mogą być różne - zwolnienie miejsca, pozbycie się błędnych danych, ukrycie czegoś itd. Jednak ta operacja jest bardzo specyficzna i z tego powodu nie zawsze możliwa. Co jest powodem i czy faktycznie usuwanie zasługuje na specjalne traktowanie? Przecież mechanicznie jest dużo prostsze od wygenerowania obiektu, prawda? Cóż, niekoniecznie.
Formy usuwania
Większość z nas, myśląc o usuwaniu, kojarzy to z fizycznym pozbyciem się informacji z bazy danych (lub pliku z systemu). I całkiem słusznie - w końcu taki jest cel skasowania czegokolwiek. Tymczasem, o ile podczas pisania dokumentacji i specyfikowaniu wymagań faktycznie używa się tego terminu pod kątem opisania funkcji systemu, o tyle nie zawsze "fizyczne" kasowanie jest zalecane.
Powody są co najmniej dwa. Pierwszy z nich to fakt ostateczności takiej operacji. Oczywiście, wiele osób powie, że tak ma być - w końcu użytkownik wybiera tę opcję nie bez powodu i chce oczyścić system. Jednak nie spodziewacie się, jak bardzo użytkownicy są czasem nieświadomi i niepewni tego, o co proszą oraz co robią w aplikacji. Niby człowiek wie, że chce usunąć, ale tak naprawdę później nachodzą go wątpliwości: "a co, jeśli to nie o ten obiekt chodziło", "a można to jakoś odzyskać", "a dlaczego to mi zniknęło" i tym podobne. Jeśli mówimy o "fizycznym" usuwaniu, to faktycznie czasem mogą zdarzyć się takie dramaty - dlatego ten rodzaj powinien być stosowany tylko i wyłącznie w przypadku prostych i "nieskomplikowanych" danych. Jednak istnieje jeszcze druga kwestia, którą należy rozważyć.
A jest nią powiązanie obiektów ze sobą i to główna przyczyna, dla której "fizyczność" nie jest rekomendowana. Wyobraźmy sobie dokument przetwarzany w systemie (jako rekord w bazie danych) i zastanówmy się, jakie atrybuty go opisują. Z pewnością wiele osób wymieni nazwę czy treść, ale jest to także m.in. autor (a więc jakiś użytkownik), osoby przypisane do edycji (ponownie użytkownicy) czy kontekst, w jakim dokument został utworzony (np. projekt czy klient, także znajdujący się w bazie danych). Ale nie tylko są to atrybuty - sam dokument może mieć swoją historię, osoby go podpisujące czy załączniki. W takim wypadku, w momencie usunięcia rekordu pliku, dzięki nierozważnie ustawionym mechanizmom systemów do zarządzania bazami danych automatycznie musiałyby zostać skasowane powiązane z nim informacje, a użytkownik straciłby np. w ogóle dojście do wpisu z historii, że wykonał jakąś operację. Może to spowodować duże szkody w systemie, jest narażone na błędy, a dodatkowo sama z siebie nie pozwoli na to baza danych (o ile odpowiednio skonfigurujemy schemat), co oznacza, że wiele operacji usunięcia stowarzyszonych obiektów trzeba wykonać ręcznie.
Dlatego dużo częściej stosuje się usuwanie "logiczne" zwane też dezaktywacją. W tym przypadku dane nie są trwale usuwane w systemie - zamiast tego oznacza się jako usunięte lub nieaktywne. Niewątpliwymi zaletami są:
- brak potrzeby kaskadowego usuwania powiązanych rekordów (prostsza obsługa),
- "wentyl bezpieczeństwa" w postaci możliwości cofnięcia się do danego rekordu,
- większe możliwości analizy, kto usunął rekord,
- od strony użytkownika jest to funkcjonalnie równoważne fizycznemu usunięciu (jeśli jest dobrze zrobione),
- jeśli usunięte dane mają nadal być widoczne przy obiektach, w których zostały wcześniej wybrane, jest to nadal możliwe.
W praktyce większość ważnych obiektów przetwarzanych w systemie jest usuwana w ten sposób, gdyż tak jest po prostu bezpieczniej, a te twory bywają w relacji z naprawdę wieloma elementami. Z drugiej strony, wymaga to sprawdzania flagi aktywności praktycznie w każdym zapytaniu bazodanowym, co trochę je wydłuża, ale przede wszystkim wymaga od programistów pamiętania o tym. Są to jednak małe wady, nieznaczące w obliczu zalet. Zwłaszcza, że można wykorzystać odpowiednie mechanizmy w architekturze lub systemie bazy danych, aby takie filtrowanie robić automatycznie.
Dezaktywowanie może być rozumiane także bardziej dosłownie - bywają obiekty, które klient chce jedynie oznaczać jako nieaktywne i móc do nich zajrzeć w każdym momencie z poziomu aplikacji. Najczęściej jest to nazywane "archiwizowaniem" i działa na podobnej zasadzie, natomiast występują dwie podstawowe różnice. Po pierwsze, interfejs umożliwia ich jawne przywracanie, a po drugie - rodzi to trochę więcej problemów z zapytaniami, gdyż trzeba zdecydować, kiedy sprawdzać daną flagę, a kiedy nie. To zależy już od konkretnych ustaleń.
Inną z form jest zamazywanie danych usuwanego obiektu, co działa jak dezaktywacja, ale nie polega na zmianie jednej kolumny w systemie - zamiast tego haszuje się odpowiednie informacje, nie pozwalając na ich klarowny odczyt. Dwa terminy, o których tutaj trzeba pamiętać, to pseudonimizacja oraz anonimizacja, a do samego tematu wrócimy w dalszej części tekstu.
Wreszcie, niekiedy występuje też coś takiego jak scalanie, które samo w sobie usunięciem nie jest, ale zwykle się z nim wiąże. Bywają systemy, do których importowane są archiwalne dane klienta z plików lub innego systemu i - jak można się spodziewać - nie zawsze są to czyste informacje. Zdarza się również, że zaimportowane obiekty zostały już wcześniej ręcznie dodane do systemu, przez co powstają duplikaty. Dlatego funkcja scalania (lub, jak częściej powiedzą informatycy, "merdżowania") bywa potrzebna, a jedną z konsekwencji jest pozbycie się duplikatu, a więc jego (tym razem) fizyczne usunięcie. To sytuacja jeszcze bardziej związana ze specyfiką systemu i implementowana dość rzadko, gdyż - co tu dużo mówić - jest bardzo złożonym zagadnieniem.
Istnieją też przypadki, w których system w ogóle nie pozwala na usuwanie obiektów, w żadnej formie. Najczęściej wynika to z kwestii prawnych, związanych z płatnościami lub po prostu świadomej decyzji właściciela oprogramowania. Przykładem może być posiadanie danych użytkownika, który podpisał umowę lub dostał fakturę. Akurat kwestię użytkowników i tak rozpatrzymy osobno, ale związane z nim przypadki to najlepiej "udokumentowane" sytuacje, w której w skrócie CRUD nie ma prawa wystąpić "D". A przynajmniej nie zawsze.
Kiedy usuwać, a kiedy dezaktywować?
Pośrednio odpowiedziałem już na to pytanie - dezaktywacja na pewno jest bezpieczniejsza, pozwala uniknąć zastanawiania się, co trzeba uwzględnić przy pozbywaniu się krotki z bazy danych i daje zabezpieczenie w postaci możliwości powrotu do danych. Dlatego zaleca się tę technikę przy wszystkich obiektach, które są:
- biznesowo istotne,
- "duże", tj. nie tylko mają wiele pól, ale też ich uzupełnianie zajmuje użytkownikowi więcej czasu,
- wiążą się z wieloma innymi obiektami,
- stanowią istotny składnik danych, które są cały czas przetwarzane w systemie (np. grupa produktów, z której korzystają towary).
Natomiast ktoś słusznie może zauważyć, że pomijając wcześniej wspomnianą konieczność uwzględnienia odpowiedniej flagi w zapytaniach, dezaktywacja ma tę wadę, iż nie pozbywa się informacji, a co za tym idzie - duże tabele, w których dane są często usuwane, mogą stać się śmietnikiem i zajmować sporo przestrzeni dyskowej. W takich sytuacjach, jeśli dotyczy to np. danych słownikowych i można przenieść referencje do nich na inne obiekty, warto pokusić się o drogę "fizyczną". Dość naturalny przykład to sytuacja, w której mamy obiekt, dla którego od razu (na jednym ekranie) zarządzamy grupą informacji. Weźmy choćby klienta i jego osoby kontaktowe - dane są przesyłane w jednej paczce i istnieją dwa podejścia do zapisu:
- Każdą przesłaną osobę kontaktową przeglądamy pod kątem tego, czy jest nowa (insert), czy edytowana (znajdujemy odpowiednią krotkę i wykonywany jest update). Jeśli jakaś osoba kontakowa wcześniej istniała w bazie, a w nowych danych jej nie ma, to ją usuwamy (fizycznie lub logicznie).
- Usuwamy (fizycznie) wszystkie dotychczas zapisane osoby kontaktowe i dodajemy na nowo krotki korzystając z przesłanego formularza. W szczególności może się zdarzyć, że usunęliśmy dwie osoby i dodaliśmy na nowo te same dwie osoby.
Ponownie - wszystko zależy od przypadku. Są takie, w których te powiązane informacje dalej są wskazywane przez inne obiekty i wówczas w grę wchodzi tylko opcja nr 1. Natomiast, jeśli nie zależy nam na zachowaniu identyfikatorów rekordów, a obiekty spokojnie mogą zostać "zresetowane", to opcja nr 2 jest zdecydowanie prostsza.
Inne trudności związane z usuwaniem
Wspomniałem o tym, że usuwanie może być bardziej złożoną sprawą, niż początkowo wydaje się klientowi. W przypadku dezaktywacji "kruczek" polega na obsłudze określonej flagi przy obiektach. W przypadku klasycznego podejścia jest to zwyczajnie obarczone pewnym ryzykiem, a co za tym idzie - potrzebą uważniejszego przetestowania różnych przypadków.
Ale należy też pamiętać o szczegółach, które zwykle umykają uwadze podczas analizy. Chociażby to, że operacje usunięcia powinny być potwierdzane przez użytkowników, co oznacza wprowadzenie okien modalnych lub innych form alertów z przyciskami "Tak" oraz "Nie". Jako że z punktu widzenia użytkownika operacja jest nieodwracalna (lub przynajmniej przywrócenie jest problematyczne), taka prośba o potwierdzenie jest potrzebna. Druga kwestia to uprawnienia - jeśli nawet użytkownik może dodawać obiekty, to nie zawsze oznacza, że może je usuwać. To oznacza zabezpieczenie metod zarówno po stronie frontendu, jak i backendu, zadbanie o wyświetlenie odpowiednich przycisków na frontendzie, a czasem nawet przeniesienie wszystkiego do specjalnie wydzielonego panelu administracyjnego.
Trzecia kwestia to logowanie, ale w znaczeniu przechowywania historii operacji na danym obiekcie. Ma to sens szczególnie dla większych, biznesowo ważniejszych tabel, w których może być potrzebne późniejsze dochodzenie do tego, które dane zniknęły, dlaczego i kto był za to odpowiedzialny. Sam temat logów wykracza poza temat tego tekstu, ale jest to kolejne zabezpieczenie na wypadek nieprzewidzianych sytuacji i pytań klienta "jak to się stało, że to mi zniknęło".
Jak to wygląda z użytkownikami?
Wspomnieliśmy już o tym, że usuwanie użytkowników należy traktować specjalnie. Z jednej strony mamy do czynienia z RODO, czyli ochroną danych osobowych oraz między innymi zapewnieniem obsługi żądania usunięcia tych informacji z systemu informatycznego. Z drugiej - fakt, że użytkownicy są zazwyczaj powiązani z ogromną ilością danych i ich fizyczne usunięcie to tragedia dla systemu. Tutaj z pomocą przychodzą trzy rzeczy.
Po pierwsze, usunięcie danych osobowych może być różnie rozumiane i istnieją prawne uzasadnienia, aby takie informacje jednak były zachowywane w określonych warunkach.
Druga i trzecia rzecz to techniki nazywane anonimizacją oraz pseudonimizacją. Obie polegają na wspomnianym wcześniej maskowaniu danych w taki sposób, aby nie można było ich odczytać bez odpowiednich uprawnień lub dodatkowych działań. Jednak różnią się sposobem maskowania. W przypadku anonimizacji mamy do czynienia z całkowitą, pseudolosową (w przypadku klasycznych systemów informatycznych trudno mówić o pełnej losowości) transformacją wartości danego pola, przez co nie da się go potem odzyskać. Z jednej strony jest to bezpieczne dla personaliów użytkownika. Z drugiej - bywają sytuacje, w których dane muszą zostać odzyskane na potrzeby działań śledczych czy procesowych. Wówczas bardziej polecana jest pseudonimizacja, która również maskuje dane, jednak z użyciem pseudolosowo wygenerowanego klucza, który znają tylko zaufane osoby i może służyć do odkodowania informacji. W praktyce więc anonimizacja od pseudonimizacji różni się tym, że klucz użyty do szyfrowania w pierwszym przypadku nie jest nigdzie zachowywany, a w drugim - jest.
W takiej sytuacji należy szczególnie pamiętać o bezpieczeństwie danych oraz ewentualnym dostępie do zakodowanych informacji tylko dla ściśle uprawnionych osób - musi mieć to uzasadnienie prawne i być zawarte w regulaminie czy polityce prywatności. Często tworzy się osobne konto administratora, który ma uprawnienia do usuwania oraz przywracania, a jego hasło jest wręcz zamykane w sejfie. Dodatkowo, z uwagi na to, że użytkownicy nadal mogą być wyświetlani w systemie przy okazji powiązania z pewnymi obiektami, należy zadbać o to, aby odbiorcom w takich sytuacjach wyświetlała się informacja o tym, iż taka osoba została usunięta. Na pewno nigdzie nie powinien pojawić się hasz, którym teraz jest imię czy nazwisko.
Remove a delete
Na koniec warto wyjaśnić jeszcze subtelną różnicę w języku angielskim pomiędzy słowami remove oraz delete. To pierwsze sformułowanie dotyczy właśnie "miękkiego" potraktowania usuwania - oznacza odseparowanie informacji, ukrycie jej przed wzrokiem osób, ale nadal zachowanie w bazie danych. Może też być użyte w momencie, kiedy dany obiekt jest "odpinany" od innego. Z kolei delete to faktyczne usunięcie, wręcz wymazanie informacji z systemu. Pierwotnie pochodzi z języka łacińskiego, w którym praprapraprzodek tego słowa, dēlētus oznaczało "zniszczony".
Podsumowanie
Jak widać, temat usuwania wydaje się prosty, ale taki nie jest. A nawet, jeśli ktoś już wcześniej spodziewał się, że dane raczej się dezaktywuje niż kasuje, to mam nadzieję, że mogłem coś dodać do tej wiedzy i sprawić, że każdy zacznie traktować tę operację jeszcze bardziej poważnie niż do tej pory.
Pozdrawiam i dziękuję - Jakub Rojek.