Zapisywanie udekorowanych obiektów do bazy danych

0

Jak powinno się zapisywać obiekty, które zostały udekorowane (decorator pattern) w bazie danych? Chcę użyć go do modelowania domeny

1

Jakiś konkretny przykład? Wydaje mi się, że do zapisywania skomplikowanych obiektów najbardziej nadają się bazy dokumentowe.

4

Nie znam się, ale raczej takie udekorowane obiekty to powinieneś trzymać w jakiejś wytwornej bazie danych:

  • sybase,
  • informix,
  • w ostateczności oracle (może nie jest zbyt elegancka, ale przynajmniej jest droga),

Na pewno nie możesz trzymać tego w jakiejś bazie dla plebsu jak MySQL czy Postgres. Jak to będzie wyglądało?

0

Modelowanie domeny nie ma nic wspólnego z bazą danych.

Poza tym, w bazie się nie zapisuje obiektów. W bazie co najwyżej możesz zapisać dane, które ewentualnie są zserializowane.

Obawiam się że padłeś ofiarą kłamstwa, że można zmapować obiekty na bazę relacyjną :/ Nie da się niestety. Baza to jest persystencja danych, z paroma fajnymi feature'ami do szukania/filtrowania i tweaki performance'owe, nic więcej. Domena jest w zasadzie tylko w kodzie źródłowym i nie wychodzi poza niego.

1
Anatolijus napisał(a):

Jak powinno się zapisywać obiekty, które zostały udekorowane (decorator pattern) w bazie danych? Chcę użyć go do modelowania domeny

Decorator pattern rozszerza interfejs poprzez otoczenie instancji tego samego interfejsu dodatkową logiką. Na czym miałoby polegać persystowanie dodatkowiej logiki w bazie danych? Musisz podać przykład, bo na ślepo nic nie poradzimy

0

@Nofenak @slsy

Podoba mi się to podejście z filmu. Dokładnie w 3:22 jest pokazana hierarchia jakiegoś przykładowego IDiscount.

new RelativeDiscountCap(0.25M,
  new NoZeroDiscounts(
    new ParallelDiscounts(new TitlePrefixDiscounts(0.17M, "C"), new RelativeDiscount(0.12M))
  )
)
3

Wydaje mi się że zadałeś nieprawidłowe pytanie. Moim zdaniem pytasz jak serializować udekorowane obiekty i nie ma myślę łatwej ani jednoznacznej odpowiedzi na to pytanie.
Nie wydaje mi się to standardowym pomysłem. Najłatwiej zapisać gdzieś na boku kolejność dekoratorów, ale musisz uważać bo takie rozwiązanie zwłaszcza z używaniem refleksji może mieć te same problemy co BinarySerializer który nie jest bezpieczny ze względu na to że można zmusić system do stworzenia dowolnych typów.
W ten sposób mała dziura pozwalająca na SQL Injection może doprowadzić do ingerencji w działanie całej aplikacji.

W rzeczywistym kodzie raczej struktura obiektów i ich dekorowania się często nie zmienia, dlatego moim zdaniem nie ma sensu serializować czegoś takiego:

new RelativeDiscountCap(0.25M,
  new NoZeroDiscounts(
    new ParallelDiscounts(new TitlePrefixDiscounts(0.17M, "C"), new RelativeDiscount(0.12M))
  )
)

tylko same parametry takie jak

0.25M, 0.17M, "C", 0.12M

i złożyć to w obiekt samemu przy deserializacji. Jeśli zserializujesz udekorowane obiekty to pozbywasz się jednej z podstawowych zalet dekoratorów - tego że możesz w każdej chwili dodać kolejną warstwę pomiędzy. Jeśli będziesz trzymał w bazie udekorowane obiekty to nie możesz tego zrobić bo będzie musiał "przedekorować" każdy obiekt w bazie.
Jeśli faktycznie masz elastyczny kod który zmienia dynamicznie kolejność i ilość dekoratorów na obiektach to pokaż konkretny przypadek.

0
Anatolijus napisał(a):

Podoba mi się to podejście z filmu. Dokładnie w 3:22 jest pokazana hierarchia jakiegoś przykładowego IDiscount.

W tym wypadku każdy dekorator ma swoją tożsamość, więc problem redukuje się do trzymania union typu w bazie danych. Możesz:

  • zrobić osobną tabelę na każdy typ zniżki
  • trzymać wszystko w jednej tabeli za pomocą enuma i jakiś dynamicznych metadanych odnośnie każdej żniżki
  • olać kolumny i trzymać informacje w JSONIe
0
slsy napisał(a):
Anatolijus napisał(a):

Podoba mi się to podejście z filmu. Dokładnie w 3:22 jest pokazana hierarchia jakiegoś przykładowego IDiscount.

W tym wypadku każdy dekorator ma swoją tożsamość, więc problem redukuje się do trzymania union typu w bazie danych. Możesz:

  • zrobić osobną tabelę na każdy typ zniżki
  • trzymać wszystko w jednej tabeli za pomocą enuma i jakiś dynamicznych metadanych odnośnie każdej żniżki
  • olać kolumny i trzymać informacje w JSONIe

Najlepiej do tego podejść tak jak pisał @obscurity , czyli zapisać tylko same parametry. Pomysł że można zapisać "tożsamość obiektów w bazie" się nie uda. W bazie zapiszesz tylko dane i nic więcej (ej, może stąd nawet jest nazwa "bazy danych"? :o).

0
Riddle napisał(a):

Najlepiej do tego podejść tak jak pisał @obscurity , czyli zapisać tylko same parametry. Pomysł że można zapisać "tożsamość obiektów w bazie" się nie uda. W bazie zapiszesz tylko dane i nic więcej (ej, może stąd nawet jest nazwa "bazy danych"? :o).

Pisałem o tożsamości, bo nie wiedziałem co to za dekorator. Równie dobrze mogłaby to być jakaś lambda implementująca interfejs i jej jedyna tożsamość to jej kod.

Zapis parametrów jest ok, to co napiszał @obscurity to IMO ekwiwalent serializowania do JSONa. Zapisać samym parametrów nie możemy, bo po typach parametrów nie możesz być w 100% pewny typu dekoratora

0
slsy napisał(a):

Zapis parametrów jest ok, to co napiszał @obscurity to IMO ekwiwalent serializowania do JSONa. Zapisać samym parametrów nie możemy, bo po typach parametrów nie możesz być w 100% pewny typu dekoratora

Od lat krąży popularna w środowisku idea czegoś takiego jak "object-relational mapping". Dość zręcznie dobrane słowo, ale całkowicie błędne. Sugeruje że można zmapować 1:1 dane w tabelce w bazie na obiekt (pewnie dlatego że oba mają pola i typy). Ale sam sobie znalazłeś przykład że to tak nie działa.

Jedyny sposób żeby zapisać obiekt w bazie, to znaleźć jakiś sposób żeby reprezentować go jak bajty, i potem odtworzyć go z tej reprezentacji. Te bajty to mogą być integer'y, może być tekst (np json, albo inny format), może być jakiś inny binarny format, etc. Ale nie da się machnąć różdżką i "magicznie" persistować obiekt.

1
Riddle napisał(a):

Jedyny sposób żeby zapisać obiekt w bazie, to znaleźć jakiś sposób żeby reprezentować go jak bajty, i potem odtworzyć go z tej reprezentacji. Te bajty to mogą być integer'y, może być tekst (np json, albo inny format), może być jakiś inny binarny format, etc. Ale nie da się machnąć różdżką i "magicznie" persistować obiekt.

Nie rozumiem wywodu i zgadzam się. To co napisałem odnosi się do tego, że 0.25M, 0.17M, "C", 0.12M to za mało informacji, bo brakuje typów użytych struktur (w końcu mówimy o dekoratorze i kolejność takiego drzewa obiektów może być różna) i potrzebujemy zapisać do bazy mniej więcej taką informację:

{
  "RelativeDiscountCap": {
    "parameter1": 0.25,
    "parameter2": {
      "NoZeroDiscounts": {
        "ParallelDiscounts": {
          "TitlePrefixDiscounts": {
            "parameter1": 0.17,
            "parameter2": "C"
          },
          "RelativeDiscount": {
            "parameter1": 0.12
          }
        }
      }
    }
  }
}
Riddle napisał(a):

Jedyny sposób żeby zapisać obiekt w bazie, to znaleźć jakiś sposób żeby reprezentować go jak bajty, i potem odtworzyć go z tej reprezentacji. Te bajty to mogą być integer'y, może być tekst (np json, albo inny format), może być jakiś inny binarny format, etc. Ale nie da się machnąć różdżką i "magicznie" persistować obiekt.

Przecież tak działają ORMy i bardziej ogólnie wszelakie automatyczne serializatory operujące na metainformacjach z kodu jak np. te używające refleksji w runtime lub compile time. Jeśli ograniczasz się do konkretnych struktur danych to jakikolwiek obiekt da się tak przestawić. Problemem jest trzymanie różnych obiektów w separacji (np. w innych tabelach), ale wrzucenie całego obiektu wraz z wszystkimi innymi obiektami jest proste.

W tym wypadku największym problemem jest dynamiczna liczba dekoratorów (zawsze można dodać kolejną implementację IDiscount) i nie znam żadnego takiego automatycznego mapowania, które robi coś takiego (pewnie dlatego, że czytelniej jest użyć jakieś sum type o określonej liczbie wariantów), ale jest to do zrobienia

0
slsy napisał(a):
Riddle napisał(a):

Jedyny sposób żeby zapisać obiekt w bazie, to znaleźć jakiś sposób żeby reprezentować go jak bajty, i potem odtworzyć go z tej reprezentacji. Te bajty to mogą być integer'y, może być tekst (np json, albo inny format), może być jakiś inny binarny format, etc. Ale nie da się machnąć różdżką i "magicznie" persistować obiekt.

Przecież tak działają ORMy i bardziej ogólnie wszelakie automatyczne serializatory operujące na metainformacjach z kodu jak np. te używające refleksji w runtime lub compile time. Jeśli ograniczasz się do konkretnych struktur danych to jakikolwiek obiekt da się tak przestawić. Problemem jest trzymanie różnych obiektów w separacji (np. w innych tabelach), ale wrzucenie całego obiektu wraz z wszystkimi innymi obiektami jest proste.

Tak miały działać ORMy, i tak obiecują ze działają - ale niestety nie działają tak.

Modele ORM to po prostu dosyć skomplikowane struktury - DTO, które mają metody do zapisania się i odczytania (insert, update, read, select, fetch), nic więcej. Na pewno nie są to obiekty, w takim rozumieniu jakich chce użyć autor wątku.

Nie da się po prostu zmapować obiektu na tabelkę w bazie. Najwyżej co możesz zrobić to zmapować strukturę na tabelkę. Więc ORM powinien się nazywać SRM - Structure-Relational Mapping. Obiekt który ma tożsamość, zaenkapsulowany stan i zachowanie to jest zupełnie inny byt niż wiersz w tabelce i nie da się ich zmapować 1:1.

0
Riddle napisał(a):

Nie da się po prostu zmapować obiektu na tabelkę w bazie. Najwyżej co możesz zrobić to zmapować strukturę na tabelkę. Więc ORM powinien się nazywać SRM - Structure-Relational Mapping. Obiekt który ma tożsamość, zaenkapsulowany stan i zachowanie to jest zupełnie inny byt niż wiersz w tabelce i nie da się ich zmapować 1:1.

Jeśli istnieje opcja, która pozwala na zmapowanie obiektu do postaci binarnej za pomocą której można potem odtworzyć obiekt to według mnie się da. Oczywiście wszystko rozbija się o szczegółu i do tego jakie chcemy interakcje. Najprostsza metoda tj. zapis stanu całego systemu do jednego ogromnego JSONa i póżniejsze odtworzenie to nie jest rocket science i o tym mówię

2
Riddle napisał(a):

Nie da się po prostu zmapować obiektu na tabelkę w bazie. Najwyżej co możesz zrobić to zmapować strukturę na tabelkę.

No w zasadzie w MSSQL możesz tworzyć własne typy kolumn w .NET - nazywa się to User Defined Type (UDT)
https://learn.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-types/creating-user-defined-types?view=sql-server-ver16

Nie widziałem tego nigdy użytego w praktyce i w rezultacie trzymamy w bazie obiekty, z tym że nadal są zserializowane więc nie wiem czy spełniają Twoją definicję obiektu bo oczywiście nie da się w nich nadal trzymać na przykład aktywnego połączenia sieciowego czy pliku (a może się mylę?).

slsy napisał(a):

Najprostsza metoda tj. zapis stanu całego systemu do jednego ogromnego JSONa i póżniejsze odtworzenie to nie jest rocket science i o tym mówię

To nie jest też nic prostego żeby to zrobić dobrze, po pierwsze będziesz miał problem z prywatnymi polami, problem z poprawną deserializacją niektórych obiektów jeśli ich do tego nie przystosujesz, problem z referencjami, zwłaszcza cyklicznymi i jak już wspominałem wcześniej problem z bezpieczeństwem systemu jeśli nie użyjesz whitelisty dopuszczalnych typów i konstruktorów.

1

Dekorator to wzorzec pozwalający na dynamiczne dodawanie zachowań do istniejących obiektów.
W bazie danych przechowuje się dane, nie zachowania.

Wniosek jest chyba oczywisty.

1 użytkowników online, w tym zalogowanych: 0, gości: 1