Trzymanie niezmienników biznesowych

Trzymanie niezmienników biznesowych
CY
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10
0

Mamy zwykłą aplikację typu API udostępniane klientom. API zapisuje rekordy/obiekty do dowolnej, przyzwoitej bazy SQL. API może być uruchiomone w wielu instancjach, wiele klientów może pracować jednocześnie na tym samym rekodzie/formatce.

  1. Powiedzmy mamy rekord użytkownika w bazie z regułą biznesową, że może być jeden użytkownik/wpis per email.
    W jaki sposób to osiągnąć tą unikalność aplikacyjnie, nie bazodanowo - bez wrzucana constrainów do DDL?
  2. W jaki sposób zabezpieczyć się przed sytuacją, że klient otwiera formatkę, czeka 1h, w międzyczasie zmienił się obiekt z formatki i klient zedytuje już nieaktualną wersję obiektu?
Charles_Ray
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1951
3

Ad. 1: Odpowiedz uproszczona brzmi: nie da się. Skąd to wymaganie, że nie można tego sprawdzać na poziomie bazy danych? Po prostu użyj transakcji bazodanowych przy odpowiednim stopniu izolacji i z głowy.

Odpowiedź zaawansowana: próbujesz zaimplementować atomową operację “compare and set” pomiędzy wieloma procesami. Każda aplikacja to osobny proces - z osobnymi zasobami, w tym z osobną pamięcią. Możesz albo “outsource’ować” ten problem do bazy danych (constrainty, transakcje), albo bawić się w jakiś algorytm rozproszonego consensusu (np. Paxos, Raft). Nie chcesz tego robić :D

Ad. 2: Transakcje i pessimistic lub optimistic locking

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1185
0

Powiedzmy mamy rekord użytkownika w bazie z regułą biznesową, że może być jeden użytkownik/wpis per email.
W jaki sposób to osiągnąć tą unikalność aplikacyjnie, nie bazodanowo - bez wrzucana constrainów do DDL?

Otwierasz tranzakcję o odpowiednim poziomie izolacji. W tej samej tranzakcji najpierwsz wołasz zapytanie, które sprawdza czy się wszystko zgadza a następnie dodajesz/aktualizujesz informację z bazie

Oczywiście jest to bardzo złożony temat, tranzakcje są ciężkie do użycia i zrozumienia jak każdy rodzał współbieżnego kodu. Dlatego dobre constrainy na bazie są dużo lepszym pomysłem

Jak chcesz się to bawić to polecam przeczytać https://www.postgresql.org/docs/current/transaction-iso.html lub odpowiednik dla twojej bazy danych. Jak nic z tego nie rozumiesz to lepiej się za to nie bierz i spróbuj prostsze metody

W jaki sposób zabezpieczyć się przed sytuacją, że klient otwiera formatkę, czeka 1h, w międzyczasie zmienił się obiekt z formatki i klient zedytuje już nieaktualną wersję obiektu?

Możesz w formatce jak i w obiekcie trzymać wersję obiektu (najprościej będzie inkrementować go za każdym razem albo użyć jakiegoś timestampa). Jeżeli jest różnica to znaczy, że coś się wydarzyło. To jest optimistic locking

Oczywiście jest dużo innych opcji, ale optimistic jest chyba najprostrzy do zrozumienia i użycia

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
3

Większość apek używa redisa lub innej prostej redundantnej bazy in memory do trzymania unikalności username / email. Nawet nie potrzebujesz locka jak radzi gemini, po prostu wstawiasz wartość do bazy in memory i jak się udało to też do bazy danych, a jak nie to zwracasz błąd. Sprawdzanie czy username / email już jest zajęty jest natychmiastowe i bezbolesne dla głównej bazy danych.

Czyli w zasadzie sprowadza się to do tego że masz jedno miejsce prawdy w jednej instancji i wszystkie instancje pytają to miejsce zamiast odpytywać właściwą bazę. Jakże błystkotliwie. Każdy problem wielowątkowości można rozwiązać sprowadzając go do jednego wątku...

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2067
3
obscurity napisał(a):

Czyli w zasadzie sprowadza się to do tego że masz jedno miejsce prawdy w jednej instancji i wszystkie instancje pytają to miejsce zamiast odpytywać właściwą bazę.

Nie jedno źródło prawdy, bo właśnie wprowadziłeś drugie 😛 co, jeśli aktualizacja Redisa/memcached się uda, ale np jakaś awaria sieci spowoduje że transakcja bazodanowa nie zostanie zatwierdzona? Będziesz mieć inny stan w dwóch miejscach. Żeby to nie wybuchało w rękach ani nie wymagało którego TTLa / interwencji na cache w razie rozjazdu, musiałbyś pewnie również pisać tylko do jednego źródła prawdy, a drugie odtwarzać ze zmian na drugim stosując np outbox lub event sourcing. Choć nadal tymczasowa i krótkotrwała niespójność mogłaby mieć miejsce i wpływać na użytkownika.

Znacznie bardziej przemawia do mnie optimistic lock niż mnożenie źródeł prawdy.

OP jak ciekawią Cię po prostu możliwe rozwiązania, jeśli scenariusz zakłada, że użytkownik zmienia dane współdzielone, a jednocześnie może mieć kopie danych sprzed wielu godzin (powiedzmy że system działa też offline) dość ciekawą strukturą są CRDT. To zasadniczo struktury danych stworzone pod przypadek zbliżony do Twojego, choć kojarzą się głównie z edycją dokumentów tekstowych.

JG
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 287
0

@superdurszlak dlatego tak Gemini napisał: niski TTL w Redisie i unikalność na bazie powinna też być jednak. I w ogóle - to jest problem wydumany, bo powininno być pole unikalne i tyle, a tu widzimy tworzenie sobie samemu problemu i potem obmyślanie jak go rozwiązać.

@cerrato co z tego że treść wygenerowana przez llm, skoro odpowiedź była sensowna 😁

CY
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10
0

Dużo było pytań czemu nie dorzucić constraintów na bazie - czasami łatwiej napisać coś swojego niż prosić się i przekonywać projektantów o zmianie w bazie 😉 Dlatego się zastanawiam.
Jak rozumiem użycie constraintów to powszechnie używane rozwiązanie? Nawet w przypadku, nie wiem - jakichś walidacji na kilku tabelkach typu:

  • w tabeli głównej unikalność na kilka kolumn, dodatkowo w tabeli podrzędnej musi być dokładnie N wierszy powiązanych (np. rekord główny dla dnia per klient plus w tabeli podrzędnej jedna wartość dla kaźdej minuty w dobie?)

Nasuwa mi się pytanie - po co wtedy aplikacja? Jeśli baza i tak całą logikę biznesową by trzymała.
@Charles_Ray w sumie pessimistic lock wystarczy? Jeśli zablokujemy rekord na transakcję, to po końcu transakcji, kolejna aktualizacja "zakolejkowana" się wykona bez błędu. Chyba że w połączeniu z optimistic locking to OK.

CY
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10
0

@Miang a jeśli wszystko ma siedzieć w bazie - to jak obsłużyć przypadek typu:
mamy tabelę kupon z polami ilość użyć kuponu i maksymalna ilość użyć. Oczywiście constraint byłby, że liczba użyć kuponu nie może być większa niż maksymalna ilość użyć.
To teraz dorzucamy, że OK, ta zasada dla wszystkich tylko nie przykładowo żony prezesa? Co wtedy? Zakładamy, że nie mamy tabeli z użytkownikami, którzy wykorzystali dany kupon.

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2067
3
cyclicgraph napisał(a):

Dużo było pytań czemu nie dorzucić constraintów na bazie - czasami łatwiej napisać coś swojego niż prosić się i przekonywać projektantów o zmianie w bazie 😉 Dlatego się zastanawiam.
Jak rozumiem użycie constraintów to powszechnie używane rozwiązanie? Nawet w przypadku, nie wiem - jakichś walidacji na kilku tabelkach typu:

  • w tabeli głównej unikalność na kilka kolumn, dodatkowo w tabeli podrzędnej musi być dokładnie N wierszy powiązanych (np. rekord główny dla dnia per klient plus w tabeli podrzędnej jedna wartość dla kaźdej minuty w dobie?)

Nasuwa mi się pytanie - po co wtedy aplikacja? Jeśli baza i tak całą logikę biznesową by trzymała.
@Charles_Ray w sumie pessimistic lock wystarczy? Jeśli zablokujemy rekord na transakcję, to po końcu transakcji, kolejna aktualizacja "zakolejkowana" się wykona bez błędu. Chyba że w połączeniu z optimistic locking to OK.

Bo wszystko jest trade-offem, na przykład bazy są trudne w utrzymywaniu i wersjonowaniu, a SQL i procedury składowane jest tak średnio ekspresyjny do opisywania skomplikowanej logiki za pomocą abstrakcji, a nie deklaratywnych operacji typu a teraz zrób okno....

Podałeś dobry przykład - masz wąskie gardło na komunikacji z zespołem, który projektuje wam bazę danych. Czyli zgodnie z prawem Conwaya, jeśli komunikacja z tym zespołem jest do bani, będziecie pewnie naturalnie dążyć do ograniczania interakcji z nimi, czyli np. rozwiązania software'owe mogą być atrakcyjne nawet, jeśli są technicznie trudniejsze do wykonania poprawnie 😉

Na marginesie, skoro baza to broszka innego zespołu, to macie mocno ograniczone pole manewru. Dlaczego nie-wasz zespół w ogóle narzuca wam projekt? To trochę podejrzane.

Constrainty są w mocno znormalizowanych bazach, tylko trzeba wtedy uważać np. na kaskadowe usuwanie rekordów, by nie usunąć sobie za dużo. Indeksy są powszechne i są podstawą. Z taką agresywną normalizacją spotykam się rzadko, zazwyczaj to przede wszystkim constraint unique na kluczu głównym i na mapowaniach many-to-many w tabelach pomocniczych, o ile jakieś są, jakieś klucze obce, a większość danych żyje w kolumnach dokumentowych np. JSONB. Widoków używa się w moim doświadczeniu umiarkowanie często. Widoków zmaterializowanych z rzadka, ale czasami się przydają (np. jak potrzebowaliśmy niekoniecznie najnowszy zestaw danych "policzonych" z zawartości ETLa - materialized view był do tego idealny). Procedury składowane są dość częste, ale to istny utrzymaniowy potworek.

Charles_Ray
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1951
2

@cyclicgraph Zastanawiam się czy Ty starasz się rozwiązać problem techniczny czy obejść problem organizacyjny?

Zespół robiący backend powinien mieć wpływ na bazę danych i swobodnie kształtować jej schemat pod wymagania biznesowe i techniczne (np. wydajnościowe). Nie mówiąc już o tym, że zespół powinien być w stanie autonomicznie dowozić ficzery across the stack: frontend, backend, baza, etc

Kiedyś pracowałem na takim projekcie, gdzie bazę danych ogarniali DBA, a my musieliśmy tłumaczyć jak działa Hibernate/JPA i wpasowywać się w ich pomysły. Tak się robiło soft 15 lat temu, nie polecam.

Miang
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1944
0
cyclicgraph napisał(a):

@Miang a jeśli wszystko ma siedzieć w bazie - to jak obsłużyć przypadek typu:
mamy tabelę kupon z polami ilość użyć kuponu i maksymalna ilość użyć. Oczywiście constraint byłby, że liczba użyć kuponu nie może być większa niż maksymalna ilość użyć.
To teraz dorzucamy, że OK, ta zasada dla wszystkich tylko nie przykładowo żony prezesa? Co wtedy? Zakładamy, że nie mamy tabeli z użytkownikami, którzy wykorzystali dany kupon.

mi nie chodzi o to ze wszystko ma siedzieć w bazie. mi chodzi o to że ma wszystko być wspomniane w projekcie a napisałeś

niż prosić się i przekonywać projektantów o zmianie w bazie

więc wygląda na to że problemem jest komunikacja z projektantami a nie sama baza

btw. bardziej skomplikowane warunki w bazie można obsłużyć funkcjami w tej bazie

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2067
1
Charles_Ray napisał(a):

@cyclicgraph Zastanawiam się czy Ty starasz się rozwiązać problem techniczny czy obejść problem organizacyjny?

Zespół robiący backend powinien mieć wpływ na bazę danych i swobodnie kształtować jej schemat pod wymagania biznesowe i techniczne (np. wydajnościowe). Nie mówiąc już o tym, że zespół powinien być w stanie autonomicznie dowozić ficzery across the stack: frontend, backend, baza, etc

Kiedyś pracowałem na takim projekcie, gdzie bazę danych ogarniali DBA, a my musieliśmy tłumaczyć jak działa Hibernate/JPA i wpasowywać się w ich pomysły. Tak się robiło soft 15 lat temu, nie polecam.

Jako że działam w Platform Team to się wypowiem.

Czasem zajmujemy się dostarczaniem baz zespołom aplikacyjnym, jeśli naprawdę nie ogarniają self-service - niestety bywa że Platform to tak naprawdę taki klik-ops i interweniowanie bo ktoś ma czerwony pipeline. Natomiast co do zasady, to np. schemat bazy danych nas kompletnie nie interesuje, my się martwimy np. ograniczaniem nadmiernych dostępów do baz, szczególnie do danych wrażliwych, jakimiś backupami i automatyzacjami pod disaster recovery, zapewnieniem żeby to się skalowało i nie kosztowało przy tym fortuny. Jakie kto tabelki sobie tam powkłada, to prawdę mówiąc nie nasza sprawa, tylko zespołów od aplikacji. DBA nie mamy, projektantów baz danych nie mamy. Częściowo te obowiązki rozkładają się na Platform i zespoły aplikacyjne, ale nie mamy odrębnego gremium które rysuje sobie tabelki i narzuca je tym, którzy będą na nich pracować.

Jak masz wpływ na rozwiązanie zgrzytu organizacyjnego, ogarnij zgrzyt organizacyjny i potem myśl które rozwiązanie techniczne jest najlepsze w danym przypadku. Jeśli na zgrzyt organizacyjny nie masz wpływu, no to trudno, wybierasz rozwiązanie techniczne wystarczająco dobre dla problemu, które przy okazji minimalizuje wpływ zgrzytów organizacyjnych i tyle.

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
4
cyclicgraph napisał(a):

Nasuwa mi się pytanie - po co wtedy aplikacja? Jeśli baza i tak całą logikę biznesową by trzymała.

Nie - logika biznesowa jest w aplikacji, w bazie są dane - zgodne z formatem i spójne.
Każde inne rozwiązanie to strata czasu i energii.

CY
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10
0

Rozumiem - w takim razie gdzie kończy się odpowiedzialność aplikacji, a zaczyna bazy danych? O co powinna dbać baza, a o co aplikacja? Chętnie zapoznam się z przykładami.
Wyżej rownież dałem przykładów mnie nurtujących.
Czy walidację mamy powtarzać - aplikacja waliduje, ale baza ostatecznie ma analogiczne walidacje (lub więcej) i trzyma tą ostateczną spójność?

Plus jak to ma się to podejście do cytatu z Designing Data-Intensive Applications (rozdział 7, Transakcje) - szczegółnie zdanie w nawiasach?

However, this idea of consistency depends on the application’s notion of invariants,
and it’s the application’s responsibility to define its transactions correctly so that they
preserve consistency. This is not something that the database can guarantee: if you
write bad data that violates your invariants, the database can’t stop you. (Some spe‐
cific kinds of invariant can be checked by the database, for example using foreign key
constraints or uniqueness constraints. However, in general, the application defines
what data is valid or invalid — the database only stores it.)

Dodatkowo, jest coś takiego jak advisory lock - nakładając taką blokadę przy tworzeniu/edycji użytkownika na naszego emaila, mamy pewność, że do sekcji krytycznej wejdzie tylko jedna transakcja, a druga odczyta już stan z istniejącym danym adresem email. Czyli pasuje. Pewnie przy większej liczbie takich warunków, takie rozwiązanie zrobiłoby się kłopotliwe, np. wystąpiłoby ryzyko deadlocka, albo samo znalezienie klucza na którym zakładalibyśmy blokadę byłoby niemożliwe. Opinia?

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
1
cyclicgraph napisał(a):

Rozumiem - w takim razie gdzie kończy się odpowiedzialność aplikacji, a zaczyna bazy danych? O co powinna dbać baza, a o co aplikacja? Chętnie zapoznam się z przykładami.

Baza dba o to, czy dane są spójne, i czy typy danych się zgadzają. O tym, czy dane mają sens dba aplikacja.

Czy walidację mamy powtarzać - aplikacja waliduje, ale baza ostatecznie ma analogiczne walidacje (lub więcej) i trzyma tą ostateczną spójność?

Nie ma sensu, aby walidacja była powtórzona. No chyba, że aplikacja nie jest jedynym źródłem danych dla bazy.

Plus jak to ma się to podejście do cytatu z Designing Data-Intensive Applications (rozdział 7, Transakcje) - szczegółnie zdanie w nawiasach?

However, this idea of consistency depends on the application’s notion of invariants,
and it’s the application’s responsibility to define its transactions correctly so that they
preserve consistency. This is not something that the database can guarantee: if you
write bad data that violates your invariants, the database can’t stop you. (Some spe‐
cific kinds of invariant can be checked by the database, for example using foreign key
constraints or uniqueness constraints. However, in general, the application defines
what data is valid or invalid — the database only stores it.)

No ja tu nie widzę żadnej niezgodności z moimi poglądami.

Dodatkowo, jest coś takiego jak advisory lock - nakładając taką blokadę przy tworzeniu/edycji użytkownika na naszego emaila, mamy pewność, że do sekcji krytycznej wejdzie tylko jedna transakcja, a druga odczyta już stan z istniejącym danym adresem email. Czyli pasuje. Pewnie przy większej liczbie takich warunków, takie rozwiązanie zrobiłoby się kłopotliwe, np. wystąpiłoby ryzyko deadlocka, albo samo znalezienie klucza na którym zakładalibyśmy blokadę byłoby niemożliwe. Opinia?

Jeśli w bazie będzie unique na emailu, to nie wstawi się drugi raz tego rekordu, nie trzeba żadnych locków.

CY
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10
0

Mało konkretna ta odpowiedź...

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1185
1

Według mnie podział na to zawsze robi aplikacja/to zawsze robi baza jest szkodliwy, bo nic nie wnosi i rozmywa trochę sens. KAŻDA walidacja po stronie bazy to jest jakaś część logiki biznesowej, bo w zależności od tego dane zapytanie aplikacyjne się wywali bądź też nie i aplikacją musi to jakoś obsłużyć. IMO warto iść to w praktyczny sposób tj.

  • aplikacja waliduje to co na bazie jest trudne do sprawdzenia. W większości przypadków jest to praktycznie wszystko, bo kod aplikacyjny jest dużo prostszy do napisania/zrozumienia/monitorowania itd
  • baza waliduje to co jest ciężko sprawdzić w aplikacji.

Dobre przykłady walidacji po stronie bazy:

  • system typów; to tak oczywiste, że nikt o tym nie myśli. Podobno SQLite bez jakiś tam flag nie uznaje tego za pewnik xd
  • constrainty, zwłaszcza te zapewniające spójność modelu np. pilnowanie, żeby przypadkiem nie usunąć wiersza, który jest referencjowany z innych tabel
  • oczywiste checki np. update_at nie może się cofać w czasie albo nie powinień być ruszany przez kogokolwiek, bo zajmuje się tym trigger
  • specyficzne przypadki użycia bazy. Jeżeli mamy wiele aplikacji bądź też ręcznych klepaczy SQLa na produkcji to dodatkowe constrainty mają sens choć oczywiście nie jest to sytuacja idealna

IMO dobra zasada kciuka to wrzucam logikę do bazy jak ułatwia mi to życie i przewiduję, że ta logika nie zmieni się w przyszłości. Dobrą analogią są asercje z języków programowania, które są przydatne w czasie developmentu/testowania i jak stanie się coś naprawdę tak nieprzewidywalnego, że problem na 99.9% jest gdzieś indziej

Podobne argumenty widzę jak ktoś próbuje sobie tłumaczyć świat, że wyjątki są do sytuacji wyjątkowych a błędy nie w takich językach jak Java, gdzie od wyjątków chcąc nie chcąc nie da się uciec a o wyjątkowości problemu decyduje konsument a nie producent wyjątków. Niestety nie taki był cel projektowy Javy i taka niejasna definicja kompletnie nie pomaga. Lepiej jest zdefiniować jaka jest dokładna strategia użycia błędów i pogodzenia tych dwóch mechanizmów w konkretny i usystematyzowany sposób

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
1

Rozumiem - w takim razie gdzie kończy się odpowiedzialność aplikacji, a zaczyna bazy danych? O co powinna dbać baza, a o co aplikacja? Chętnie zapoznam się z przykładami.

Najlepiej żeby baza danych robiła po prostu najmniej jak może, każdy constraint, każda składowana procedura, każdy trigger to rzeczy które ciężko debugować, ciężko testować, ciężko migrować, ciężko backupować i przywracać, ciężko zmienić silnik bazy danych, dokłada obciążenie serwera bazy danych, spowalnia inserty i update'y, i ciężko o tym wszystkim pamiętać. Po latach gdy cały zespół deweloperów się zmienił dane w bazie się magicznie zmieniają i po dwóch dniach szukania okazuje się że gdzieś jest jakiś zapomniany trigger odpalany przez równie zapomniany task.

Natomiast unikalność pól lepiej zostawić bazie danych.
Czemu w ogóle chcesz żeby e-mail był unikalny? Gmail indeksował dane adresem e-mail a teraz po latach mają problem bo chcą wprowadzić możliwość zmiany adresu i jest to mega wyzwaniem. Coraz więcej serwisów pozwala tworzyć wiele kont na jedno konto e-mail bo e-mail zaczął wypierać loginy a ludzie nie chcą zakładać dodatkowych skrzynek tylko po to żeby mieć wiele kont. Najgorsze jak "niezmienniki" się zmieniają.

cerrato
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 9282
2

Standardowa odpowiedź brzmi "to zależy" ale ja bym zrobił to tak, że na serwerze postawić jakieś API czy innego pośrednika, który się komunikuje z bazą.

Aplikacja bezpośrednio nie gada z bazą, wiec mamy po pierwsze separację, a po drugie - warstwę abstrakcji. Także jakaś późniejsza zmiana bazy, jakaś migracja itp. z punktu widzenia aplikacji przechodzi bezboleśnie. Ona cały czas wali w ten sam endpoint/odpytuje ten sam adres, a co jest dalej to nie jest problem aplikacji.

Pytanie jest takie - co konkretnie oznaczają dla Ciebie te "niezmienniki biznesowe"? Odnosząc się do Twoich przykładów:

  • jeden użytkownik z jednym adresem e-mail - tutaj API/pośrednik temat załatwia. Aplikacja wysyła do tego "czegoś" żądanie dodania użytkownika z danym mailem i w odpowiediz dostaje "OK" albo "sorry, mail jest już wykorzystany". Sama aplikacja niczego nie musi robić, nie musi znać zasad dot. emaili itp. po prostu - przekazuje żądanie dalej, a potem patrzy co dostała
  • jednoczesna edycja - tutaj robimy blokadę danego rekordu/typu danych/określonej kartoteki. W sensie - jak jakiś użytkownik zaczyna edytować kartotekę klienta "4p sp. z o.o." to gdy inny spróbuje zrobić to samo to dodaje info, że aktualnie rekord jest w edycji i pokazujemy tylko w trybie do odczytu.

Tworząc pośrednika (nazwij go jak chcesz, niech to będzie API) zyskujesz to, że masz jedno miejsce trzymania logiki biznesowej.
Oczywiście - dobrze jest mieć tez jakąś podstawową walidację po stronie apki, żeby w polu "wzrost" nie wpisać "45-390 Bytom" albo w miejsce liczby zamówionych sztuk nie wpisać "Tomasz". To są takie podstawowe kwestie, ale bardzie zaawansowana logika (że np. liczba zamówionych sztuk musi być wielokrotnością 15 a także że łączna masa zamówienia nie może przekraczać 400kg) niech będzie jedynie po stronie API a nie aplikacji.

Plusem jest to, że wtedy masz jedno miejsce trzymania takiej logiki. I nieważne, co się z tym łaczy - apka webowa, apka mobilna, desktop, a może jakaś integracja z programem księgowym itp. - masz dokładnie jedno miejsce kontrolujące całość, więc nie ma ryzyka, że logika apki na Androida się rozjedzie z tym, na co pozwala aplikacja desktopowa.

A jeszcze wracając do przykładu z unikalnym emailem:

  • trzymanie tego na bazie jest problemem. Bo co w scenariuszu, w którym ktoś miał kilka lat temu konto ma określonego maila, potem je skasował, a teraz chce na nowo. Samo sprawdzenie czy mail jest w bazie spowoduje, że nie da się założyć
  • aplikacja (każda - mobilna, desktopowa, webowa itp.) musi więc mieć znajomość struktury bazy i np. wysłać zapytanie SELECT adres-email WHERE jest-aktywny=TRUE. To po pierwsze powoduje spięcie implementacji apki ze struktura bazy, po drugie - jakby np. baza się zmieniła, ale apka będzie stara to albo nie będzie działać, albo spowoduje błędne/losowe działanie
  • mając API - warunki sprawdzania emaili masz tylko tam, a aplikacja wysyła do API prośbę o dodanie usera i patrzy, co się stanie. Aplikacja nie wie i nie powinna sie interesować tym, czy adres jest już wpisany (ale oznaczony jako nieaktywny), czy go nie było, a może nie było ale przed chwilą ktoś dodał itp. Po prostu - podany mail albo moze być, albo nie może.

Cała logika powinna być po stronie serwera ALE NIE baza to ogarnia. Baza (jak pisał @somekind) odpowiada za trzymanie danych i zapewnienie ich spójności. Logika biznesowa leży poza bazą, ale niekoniecznie musi być zaszyta wprost w apce. Uważam, że najlepiej jest dać warstwę pośrednią, która trzyma logikę w oddzieleniu od bazy oraz samej aplikacji/UI.

JG
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 287
0

A powiedzcie to Oracle: tam procedery, funkcje i pakiety są tak rozbudowane, że cała logika może być tam.

Tak btw, skoro baza ma tylko "trzymać dane i dbać o ich spójność" to w ogóle niepotrzebnie wymyślono procedury, bo one przetwarzają dane zgodnie z jakaś logiką a tego przecież ma w bazie nie być

cerrato
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 9282
1

@John Gordon - to, że coś można, nie znaczy że sie powinno i/oraz/lub że warto. Jak pisałem parę dni temu na forum w innym wątku - to, że można wsadzić penisa do blendera, wcale nie oznacza, że to dobry pomysł.

Istnieją procedury składowane, triggery itp. i można z nich korzystać - ale nie znaczy to, że zawsze jest to najlepsza opcja.
To zależy od scenariusza, od tego kto i jak się z bazą łączy, jakie dane przetwarza, jakie procesy są ogarniane itp.

Tak samo jak można w samochodzie wyłaczyć ESP i inne systemy wspomagające/asystujące. Na ogół jest to kiepski pomysł, ale jednak czasem (zwłaszcza jak to robi doświadczony kierowca, który wie, co robi) może to być dobre rozwiązanie. Ale co do zasady - jak auto ma ESP, adaptacyjny tempomat i kontrolę pasa to lepiej to zostawić właczone

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
3
John Gordon napisał(a):

A powiedzcie to Oracle: tam procedery, funkcje i pakiety są tak rozbudowane, że cała logika może być tam.

czemu Oracle, chyba w każdym silniku baz daynch tak można od dekad, to podejście jest starsze od spopularyzowania testów, dlatego zdążyło wyrobić sobie wrogów. W MSSQL można nawet programować własne typy danych w C#

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
1

Procedury w bazach istnieją, bo:

  1. wsteczna kompatybilność - skoro w latach 80tych i 90tych tyle ich natworzono, to do tej pory są wspierane;
  2. są sytuacje, w których się przydają.

To nie znaczy, że należy wybierać je na domyślne rozwiązanie we współcześnie tworzonych systemach.
Zresztą, baza relacyjna jako pierwszy wybór w 2026 też jest zacofanym podejściem.

cyclicgraph napisał(a):

Mało konkretna ta odpowiedź...

Czego w niej brakuje?

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.