Czy istnieją przykłady podejść innych niż CRUD?

14

Jestem programistą, ale pracowałem również jako archiwista. Jako archiwista wiele zależy od przechowywania danych.

Często kłócę się z kolegami, jeśli chodzi o operacje na danych. Nie bardzo lubię U i D w CRUD. Zamiast aktualizować rekord Wolę dodać nowy i mieć odniesienie do starego rekordu. W ten sposób budujesz historię zmian. Nie lubię też usuwać zapisów, ale raczej zaznaczam je jako nieaktywne.

Czy istnieje na to termin? Zasadniczo tylko tworzenie i odczytywanie danych? Czy są przykłady takiego podejścia?

Pieter B.
źródło
1
Is there a term for this? Basically only creating and reading data?Pewnie, że jest: CR; P
yannis
7
Z punktu widzenia użytkownika jest to nadal CRUD. Nie znam żadnych konkretnych etykiet dla tego stylu implementacji, ale myślę, że jest to powszechne w WIELU aplikacjach. (Wymiana stosów jest dobrym przykładem ...)
Mark E. Haase
Możesz obejrzeć rozmowę zatytułowaną The Impedance Mismatch is Our Fault .
Anton Barkovsky
+1 W pewnym momencie ktoś będzie chciał raportów i bardzo trudno jest utworzyć raporty na temat danych, które nie istnieją, ponieważ zostały „zaktualizowane”. Uważam, że twoje podejście jest bardzo przyszłościowe.
Chuck Conway,
2
Możesz także obejrzeć rozmowę o Datomic: infoq.com/presentations/The-Design-of-Datomic
Marjan Venema

Odpowiedzi:

16

Oznaczenie rekordu jako usuniętego nazywa się miękkim usuwaniem . Nigdy nie słyszałem alternatywnego wyrażenia dotyczącego aktualizacji, ale myślę, że to dlatego miękko usuwasz stary rekord i tworzysz nowy.

Należy zauważyć, że jest to technika kontrowersyjna. Zobacz linki: Con vs Pro .

pdr
źródło
11

Jednym z problemów z zachowaniem historii zmian jest to, że zaśmieca ona bazę danych i może radykalnie zwiększyć jej rozmiar (w zależności od wzorców użytkowania). Dobrym pomysłem byłoby przechowywanie ścieżki audytu w oddzielnym miejscu i utrzymywanie w tabelach aplikacji wypełnionych tylko odpowiednimi danymi. Dlatego za każdym razem, gdy aplikacja wykonuje operację CRUD, zmiana jest rejestrowana w tabelach audytu, a operacja CRUD jest wykonywana na tabelach aplikacji (bez miękkich operacji usuwania).

Utrzymanie oddzielnej ścieżki audytu zapewnia nieskazitelny magazyn danych, z którym aplikacja będzie mogła współpracować, a jednocześnie zachowa historię zmian, jeśli zajdzie taka potrzeba. Możesz teraz także zarchiwizować ścieżkę audytu osobno, a nawet ją zniszczyć, w zależności od wymagań biznesowych.

System wyłączony
źródło
3
To. Również integralność referencyjna staje się koszmarem; nie można określić klucza obcego do „jednego rekordu w tej tabeli za pomocą tego nieunikalnego klucza, który nie jest oznaczony jako usunięty”. Aby obejść ten problem, należy utrwalić dane dla kopii rekordu, który ma zostać zaktualizowany, w innej tabeli, przed zaktualizowaniem tej „kopii roboczej” o nowe dane, ale nadal masz do czynienia z ogromnym rozdęciem danych.
KeithS
5

EventSourcing brzmi jak wzorzec, którego możesz szukać.

Weźmy przykład, używając prostego obiektu „car”, który chcielibyśmy śledzić kolor (następuje pseudo kod C #).

public class Car {
  public string Color { get; set; }
  public Car() { this.Color = "Blue"; }
}

W przypadku implementacji CRUD, gdy aktualizujemy kolor samochodu, poprzedni kolor zostałby utracony.

MyCar.Color = "Red";
MyCar.Save();  // Persist the update to the database and lose the previous data

Ta utrata informacji wydaje mi się tym, czego chciałbyś najbardziej uniknąć (stąd niechęć do aktualizacji i usuwania części wzorca CRUD).

Gdybyśmy przepisali klasę samochodu, aby zamiast tego reagować na zdarzenia podczas aktualizacji jej zmiany, mogłoby to wyglądać następująco:

public class Car {
    public string Color { get; private set; } // Cannot be set from outside the class

    public void ApplyEvent(CarColorChangedEvent e) {
      this.Color = e.Color;
    }
}

Jak zaktualizowalibyśmy kolor tego obiektu? Możemy stworzyć wydarzenie CarColorChanged !

var evnt = new CarColorChangedEvent("Red");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

Zauważ brak zapisu na rzeczywistym obiekcie modelu? To dlatego, że zamiast utrwalać model bezpośrednio, utrzymujemy zdarzenia, które wprowadzają model do aktualnego stanu. Wydarzenia te powinny być niezmienne .

Teraz przejdźmy do przodu i zmień kolor jeszcze kilka razy:

var evnt = new CarColorChangedEvent("Green");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

var evnt = new CarColorChangedEvent("Purple");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

Gdybyśmy spojrzeli na naszą pamięć zdarzeń (może to być relacyjna baza danych, oparta na plikach itp.), Zobaczylibyśmy szereg zdarzeń związanych z naszym obiektem samochodu:

CarColorChangedEvent => Red
CarColorChangedEvent => Green
CarColorChangedEvent => Purple

Gdybyśmy chcieli odbudować ten obiekt samochodu, moglibyśmy to zrobić po prostu tworząc nowy obiekt samochodu i stosując zdarzenia z naszego magazynu zdarzeń do tego obiektu.

var MyCar = new Car();
var events = MyDatabase.SelectEventsForCar("CarIdentifierHere");
foreach(var e in events) {
  MyCar.ApplyEvent(e);
}
Console.WriteLine(MyCar.Color); // Purple

Dzięki strumieniowi wydarzeń możemy przywrócić stan samochodu do poprzedniego okresu, po prostu tworząc nowy obiekt samochodu i stosując tylko te zdarzenia, które chcemy:

var MyCar = new Car();
var event = MyDatabase.GetFirstEventForCar("CarIdentifierHere");
MyCar.ApplyEvent(e);
Console.WriteLine(MyCar.Color); // Red
Mikrofon
źródło
5

Sourcing zdarzeń to droga, na którą powinieneś się przyjrzeć, i powinieneś rzucić okiem na to, co Greg Young ma na ten temat do powiedzenia.

http://goodenoughsoftware.net/

Zobacz także tę prezentację w jego bazie danych (Event Store). Możesz także znaleźć inne filmy.

http://oredev.org/2012/sessions/a-deep-look-into-the-event-store

Nie wybrałbym odpowiedzi „miękkie usuwanie”, chyba że potrzebujesz konkretnie możliwości wyszukiwania usuniętych elementów, ale wtedy nie powinieneś myśleć o nich jako usuniętych, ale raczej zarchiwizowanych. Myślę, że terminologia jest tutaj bardzo ważna.

Nie chciałbym też utrzymywać „tabeli wersji”. Wszystkie „tabele wersji”, jakie kiedykolwiek widziałem (w tym te, które próbuję obecnie oczyścić - 7 lat danych uszkodzonych z powodu błędów ... i nie ma sposobu, aby je odzyskać, mimo że mamy dane historyczne .. , ponieważ jest to tak samo uszkodzone), ostatecznie jest uszkodzony z powodu błędów w kodzie, a na koniec nadal tracisz dane, ponieważ nigdy nie możesz wrócić i odtworzyć danych, które zostały uszkodzone przez uszkodzenie.

W przypadku modelu pozyskiwania zdarzeń tak nie jest. Zawsze możesz odtworzyć dokładnie to, co zrobił użytkownik. Jest to bardzo ważna różnica między CRUD a Sourcingiem zdarzeń. Architektura Event Sourcing zapisuje zdarzenia w magazynie zdarzeń, a nie obiekty danych lub obiekty modelu domeny. Wydarzenie może z łatwością wpłynąć na wiele obiektów. Pomyśl o rozwiązaniu koszyka, w którym zamienisz każdy element w koszyku na rzeczywiste zamówienie. Jedno zdarzenie wpływa na wszystkie przedmioty przedmiotu, a także przedmioty koszyka, które są przekształcane w obiekt zamówienia.

Jeśli zachowałeś wersjonowaną kopię każdego wiersza w każdej tabeli w bazie danych, wyobraź sobie horror związany z koniecznością przewinięcia do określonego znacznika czasu, nie wspominając o niesamowitej ilości miejsca i narzutach związanych z utrzymaniem tej tabeli wersji.

Za pomocą Event Sourcing można łatwo przewijać do tyłu, odtwarzając zdarzenia do określonego momentu. Szybkie przewijanie do przodu można wprowadzić za pomocą migawek, ale to wszystko kwestia implementacji.

Ale prawdziwą zaletą, którą, jak sądzę, polubisz, ponieważ jesteś szczególnie zainteresowany nie utraceniem danych, jest to, że jeśli odkryjesz błąd w kodzie, który zapisuje te dane, nie musisz wracać i czyścić danych (co często jest niemożliwe, ponieważ dane prawie nigdy nie są kompletne). Zamiast tego po prostu napraw błąd i odtwórz wszystkie zdarzenia. Wtedy będziesz miał bazę danych z ładnymi poprawnymi danymi.

W przypadku debugowania, jak często prosiłeś użytkownika, aby powiedział ci, co zrobił ... dlaczego nie po prostu odtworzyć tego, co zrobił, a następnie przejść przez kod! Całkiem fajne, huh.

Mam nadzieję że to pomoże.

Jay Pete
źródło
2

Nie dokładnie twój przykład, ale w starszych systemach finansowych miałeś pamięć WORM . Jeśli potrzebujesz „zaktualizować”, zapisałeś nowy rekord i zawsze odnosiłeś się do ostatniego rekordu jako aktualny, ale żadne popełnione dane nie mogły zostać nadpisane.

Wielu ludzi przeniosło ten pomysł na późniejsze systemy. Słyszałem, że to, co opisujesz, nazywane jest tabelami WORM, ale tylko w tych kręgach.

Rachunek
źródło
2

Tak, jest dość powszechny w systemach korporacyjnych, są w zasadzie dwa podejścia:

  • „bim - czasowy”, w którym każdy rekord ma ważny od i ważny do znacznika czasu („bieżący” rekord mający do tej pory „wieczność” - null, „9999-12-31” lub pewną tak wysoką wartość). Rekordy nigdy nie są usuwane, zamiast tego data „ważna do” jest ustawiona na bieżącą godzinę, aw przypadku aktualizacji wstawiany jest nowy rekord z aktualną godziną i na zawsze ważny do tej pory.
  • „tabela historii” - za każdym razem, gdy rekord jest zmieniany, kopia starego rekordu jest zrzucana do tabeli historii / dziennika ze znacznikiem czasu zdarzenia.

Istnieją duże różnice w szczegółowości dla obu podejść. np. jeśli zmieni się ilość widżetów w elemencie zamówienia, czy zachowujecie historię dla całego zamówienia, czy tylko dla tego jednego elementu?

Ogólnie rzecz biorąc, „dwumiesięczny” to dużo dodatkowej pracy i jest tego warte tylko wtedy, gdy masz wiele przypadków użycia, takich jak „audytor otrzymuje status zamówienia na dzień 31 grudnia”.

James Anderson
źródło
-2

w zasadzie crud nie może zostać ukończony bez tych 4 rzeczy. Crud oznacza Twórz, odczytuj, aktualizuj i usuwaj, więc gdy próbujesz tylko odczytać dane, możesz użyć do tego prostej kwerendy, ale wszystkie te trzy rzeczy są powiązane z jedną i innymi prostymi koncepcjami bazy danych

Tayyab Vohra
źródło