Co nieco o soft delete przy użyciu Node.js i MongoDB

Usuwanie danych jest jedną z 4 podstawowych czynności jakie możemy w  tradycyjnych aplikacjach (zaraz po dodawaniu, odczycie i aktualizacji). Wydawać by się mogło, że nie jest to zbyt ciekawy temat. Ot wydajemy polecenie “usuń”, a nasze dane bezpowrotnie znikają. Okazuje się jednak, że nawet tak trywialną czynność jak usuwanie danych można wykonać na kilka sposobów oraz, co ciekawe, otrzymać różne rezultaty! Jednym z bardzo ciekawych sposobów na “usuwanie” danych jest soft delete. O tym dlaczego słowo usuwanie ująłem w cudzysłów dowiesz się w dalszej części tego artykułu.

soft delete - usunięte dane

Na czym polega soft delete?

Mechanizm soft delete polega nie tyle na faktycznym usunięciu danych z bazy danych, co na oznaczeniu ich jako usunięte. Najczęściej odbywa się to przez dodanie do rekordu w bazie odpowiedniej flagi. Dzięki temu nasze dane nadal pozostają w bazie danych, jednak z poziomu aplikacji są niewidoczne – tak jakby ich nie było. Korzystając z tego mechanizmu otrzymujemy szereg nowych możliwości.

Przede wszystkim w przypadku gdy użytkownik nieumyślnie usunął swoje dane możemy w bardzo prosty sposób je przywrócić. Nie ma wtedy potrzeby zaglądania do backupu (a co jeśli nie ma backupu?😉), dzięki czemu oszczędzamy czas.

Zabezpieczamy się też przed sytuacją, gdy użytkownik umyślnie usunie dane, które nie chcemy by były bezpowrotnie usunięte. Przykładem mogą być toksyczne zachowania na serwisach społecznościowych. Przykładowo użytkownik może opublikować niestosowne treści, a następnie je usunąć i liczyć na to, że będzie bezkarny. Korzystając z soft delete widzimy tak naprawdę każdy opublikowany wpis, nawet jeśli użytkownik usunie go chwilę po dodaniu.

Oprócz tego możemy wykorzystać usunięte dane do celów rozwoju aplikacji. Jeśli nasza aplikacja uczy się zachowań użytkowników, to nawet jeśli użytkownik zdecyduje się na usunięcie konta, to jego aktywność i tak może posłużyć jako kolejna próbka do uczenia się dla naszej aplikacji.

A co z wadami?

Soft delete w mojej opinii posiada dwie główne wady. Przede wszystkim magazynujemy dane, z których prawdopodobnie nigdy nie skorzystamy przez co marnujemy powierzchnię dyskową. Istnieje bardzo mała szansa, że ktoś będzie chciał odzyskać swoje dane.

Drugą kwestią są kwestie prawne. Należy pamiętać, że zgodnie z prawem użytkownik ma prawo żądać faktycznego USUNIĘCIA danych na swój temat z bazy danych. W aplikacji, z której korzysta mała ilość użytkowników nie stanowi to problemu, ponieważ można usunąć takie dane ręcznie. Problem zaczyna się pojawiać w dużych aplikacjach, gdzie takich zgłoszeń możemy otrzymywać setki. W takim wypadku tak czy inaczej jesteśmy zmuszeni do stworzenia metod do klasycznego usuwania użytkowników.

soft delete - zniszczone notatki

Przejdźmy do praktyki

Działanie soft delete pokażę na przykładzie aplikacji napisanej w Node.js, Express.js i mongoose korzystającej z MongoDB. Do obsłużenia mechanizmu wykorzystam paczkę mongoose-delete. Link do repozytorium jak i do strony paczki na stronie npm znajduje się w źródłach i materiałach dodatkowych na końcu tego wpisu.

Przede wszystkim na samym początku musimy zainstalować wspomnianą wcześniej paczkę. Następnie importujemy naszą paczkę do pliku z modelem. Potem używamy jej jako plugin. Poniżej przedstawiam przykład:


import mongoose from 'mongoose';
import mongooseDelete from 'mongoose-delete';

const userSchema = mongoose.Schema({
    name: {
        type: String,
        required: [true, 'Name field is required']
    },
    email: {
        type: String,
        unique: true,
        trim: true,
        lowercase: true,
        required: [true, 'Email field is required.']
    }
}, {
    timestamps: true
});

userSchema.plugin(mongooseDelete);

module.exports = mongoose.model('User', userSchema, 'users');

Dodatkowe możliwości

Oprócz samego uruchomienia soft delete paczka daje kilka dodatkowych opcji. Jedną z nich jest możliwość dodania do rekordu timestampa z datą usunięcia (deletedAt). Inną z możliwości jest dodanie informacji o tym kto usunął dany rekord (deletedBy). Dodatkowe parametry podajemy jako właściwości obiektu, który przekazujemy jako drugi argument metody plugin. Zmodyfikujmy nasz przykładowy kod:


import mongoose from 'mongoose';
import mongooseDelete from 'mongoose-delete';

const userSchema = mongoose.Schema({
    name: {
        type: String,
        required: [true, 'Name field is required']
    },
    email: {
        type: String,
        unique: true,
        trim: true,
        lowercase: true,
        required: [true, 'Email field is required.']
    }
}, {
    timestamps: true
});

userSchema.plugin(mongooseDelete, { deletedAt : true, deletedBy : true, overrideMethods: true });

module.exports = mongoose.model('User', userSchema, 'users');

Jak pewnie zauważyłeś oprócz właściwości, o których wspominałem pojawiła się też właściwość overrideMethods. Dzięki temu nadpisujemy domyślne metody mongoose, tak aby nasz soft delete był w nie już zaimplementowany. Jeśli chcemy aby nadpisane został tylko wybrane metody możemy zamiast wartości true przekazać tablicę z nazwami owych metod.

Sama paczka oferuje jeszcze kilka innych możliwości, takich jak chociażby tworzenie indeksów. Jeśli chcesz poznać 100% możliwości mongoose-delete to zajrzyj do źródeł na końcu wpisu. Tam znajdziesz link do prostej i przejrzystej dokumentacji. W źródłach znajdziesz również link do mojego projektu, w którym praktycznie wykorzystałem tę paczkę.

Podsumowanie

Mam nadzieję, że nauczyłeś/aś się dzięki temu krótkiemu artykułowi czegoś nowego, co wykorzystasz w swoich projektach. Jak zwykle zachęcam do pozostawienia komentarza oraz oceny wpisu. Jeśli artykuł Ci się spodobał to udostępnij go w mediach społecznościowych. Zachęcam też do zapoznania się ze źródłami.

Źródła i materiały dodatkowe: