Czy zawsze dostosowujecie sie do pomyslow TL przy review?

0
Riddle napisał(a):

Jak czytam takie posty jak wyżej to zastanawiam się jaki argument ktoś miałby postawić, żeby kogoś przekonać że jeśli się zrobi odpowiednie odseparowanie logiki od persystencji, to praktycznie wszystko o czym mówicie jest szczegółem implementacyjnym.

Ale w dyskusji nie chodzi o to, że nie da się tego zrobić dobrze, bo się da. Nikt z tym się nie kłóci (chyba). To co ja kwestionuję to czas i koszt potrzebny na stworzenie i utrzymanie takiej warstwy abstrakcji, bo w większości projektów nie będzie z tego większego zysku oprócz dodatkowej roboty.

2
markone_dev napisał(a):
Riddle napisał(a):

Jak czytam takie posty jak wyżej to zastanawiam się jaki argument ktoś miałby postawić, żeby kogoś przekonać że jeśli się zrobi odpowiednie odseparowanie logiki od persystencji, to praktycznie wszystko o czym mówicie jest szczegółem implementacyjnym.

Ale w dyskusji nie chodzi o to, że nie da się tego zrobić dobrze, bo się da. Nikt z tym się nie kłóci (chyba). To co ja kwestionuję to czas i koszt potrzebny na stworzenie i utrzymanie takiej warstwy abstrakcji, bo w większości projektów nie będzie z tego większego zysku oprócz dodatkowej roboty.

Dobrze, to jaką chcesz przykładową apke? Co ma robić i ile ma mieć modeli, 10, 20, 30? Napisze Ci ją w odpowiedni sposób, udostępnię na publiczne repo i wtedy sam możesz stwierdzić czy to jest warte czy nie warte? Jakich funkcjonalności z EF ma użyć, żeby nie można było powiedzieć że to trywialny projekt? Myślę że to rozstrzygnie spór.

Bo jak mówiłem, odpowiednie odseparowanie tak tych warstw i utrzymanie je odseparowanymi nie jest specjalnie czasochłonne - jest taki mit w IT teraz że jeśli coś ma być zrobione dobrze, to musi długo trwać.

(ps: może nie mit, ale nieprawdziwa zasada. Jest takie przekonanie że wraz ze wzrostem jakości musi wzrosnąć czas, a to niekoniecznie musi być prawda.)

Przy czym w 99% przypadków to jest kwestia po prostu umiejętności, wyćwiczenia odpowiedniego skilla, nie dodawanie niepotrzebnych powiązań, rozpoznawanie i unikanie złych schematów i nawyków. W mojej ocenie spokojnie nawet 80% złego kodu wynika z niestosowania YAGNI - to jest na tyle prosta zasada że każdy myśli że ją zna, i paradoksalnie właśnie przez to mu umykają momenty gdzie się to łamie.

1

Wetne się tu dyskusje @markone_dev
Może ktoś ma lepszy pomysł ale z takich gotowych i dobrze opisanych to gtfs jest dobrym przykładem gdzie zadajesz pytania od różnej strony.
https://developers.google.com/transit/gtfs

Danych na necie pełno jak wpiszesz gtfs {nazwa miasta} i przykładowych schematów bazy danych np link
(można wyciac fare_attribute, fare_rule, frequency, transfer jako ze mało miast to ma)

I query w stylu jakie linie zatrzymują się na danym przystanku na dany typ dnia(z gwiazdka o której), ile trwa przejazd daną linią od początku do końca i jaka jest trasa lini. (dana linia moze miec rozne varianty przebiegu)

Nie, nie jest mi to potrzebne produkcyjnie robie w industry/telekomy ;p. Ale dawno temu próbowałem coś z tym zrobić i średnio mi to wyszło XD. Miałem głównie problem z tym, że za dużo danych musiałem agregować z bazy zawsze. Albo robić wiele obiektów na to samo.
Ale bym z ogromną chęcią zobaczył model zrobiony przez artystę do tego. Bo oddaje wiele problemów produkcyjnych.

3
Riddle napisał(a):

Dobrze, to jaką chcesz przykładową apke? Co ma robić i ile ma mieć modeli, 10, 20, 30? Napisze Ci ją w odpowiedni sposób, udostępnię na publiczne repo i wtedy sam możesz stwierdzić czy to jest warte czy nie warte? Jakich funkcjonalności z EF ma użyć, żeby nie można było powiedzieć że to trywialny projekt? Myślę że to rozstrzygnie spór.

Nie musisz mi nic pisać bo od lat siedzę w dotnecie i takie rzeczy umiem sobie napisać sam i wiem jak to zrobić dobrze. Wiem też, że aby coś zrobić dobrze potrzebne są nie tylko umiejętności ale i czas za który ktoś musi zapłacić..

Tak jak napisałem nie widzę sensu w utrzymywaniu dodatkowych abstrakcji tam gdzie nie są one potrzebne i kropka. Takie mam zdanie i nic z tym nie zrobisz. Nie musisz zawsze i wszędzie wszystkim udowadniać, że to Ty masz rację. Jest ładna pogoda, warto po pracy wyjść na spacer, basen czy pojeździć na rowerze zamiast klepać aplikacje by komuś coś udowadniać w internecie :P

2

Pracowałem z różnymi agentami, którzy aż nadto byli przekonani o swojej zajebistości. Podczas upominek gdy kończyły się argumenty potrafiło padać stwierdzenie, że on tutaj jest liderem technicznym i to on będzie decydował, bo nie bez powodu akurat on został (że niby za wiedze XDDDD).

Rada? Odpuścić. Jeśli taki zamknięty umysłowo dureń nie potrafi współpracować i przyjmować konstruktywnej krytyki to IMO lepiej zmienić projekt. Dla własnego zdrowia psychicznego.

Jeden z kwiatków z kiedyś tam :DDDD No bo przecież kolekcja sama w sobie już nie jest Iterable i trzeba podwójnie definiować iterator.

screenshot-20230718132610.png

0

@markone_dev: To jaką architekturę proponujesz do systemów średniej złożoności(aka przerośnięty CRUD ze sporą dawką dziwnej logiki biznesowej, powiedzmy że integracja z innymi systemami niewielka albo wcale). Ja widziałem takie projekty pisane na różne sposoby. Warstwa serwisowa, brak serwisów i sklejanie sql z updatami w pamięci itd. Wszystkie kończyły tak samo. Ciężko było coś zmienić ze względu na to że logika biznesowa była nieuchwytna, ot każda operacja to zwykła transformacja danych, zero abstrakcji, tu się doda ifa, tu dokleji trochę dynamicznego sql. Potem ktoś robi rozszerzenie albo nowy feature i zapomina o kilku ważnych warunkach biznesowych bo były gdzieś ukryte w jakimś serwisie w ifie do którego nawet nie zajrzał.

1
markone_dev napisał(a):

Tak jak napisałem nie widzę sensu w utrzymywaniu dodatkowych abstrakcji tam gdzie nie są one potrzebne i kropka.

Dobre abstrakcje to nie tylko lepsza czytelność ale też większe możliwości. Przykładowo mając czysty interfejs repozytorium mamy dostęp do takich tricków jak np. repozytorium cachujące, które jest zaimplementowane jako dekorator używający jakiegoś redisa. Albo to wszystko możemy obtoczyć jeszcze jednym repozytorium używającym jakiegoś super szybkiego cache w pamięci procesu.

1

@TTA120: Po pierwsze to co opisujesz nie ma nic wspólnego z wątkiem powyżej odnośnie dodatkowej abstrakcji nad ORM-em jak EF Core czy NHibernate czy osobne modele dziedzinowe i persystencji. To tak dla uporządkowania dyskusji.
Po drugie używasz bardzo ogólnej definicji złożoności projektu, która kompletnie nic mi nie mówi. Architekturę rozwiązania dobiera się na podstawie wymagań funkcjonalnych i niefunkcjonalnych, czasu i budżetu, a i tak to nie gwarantuje sukcesu doboru architektury. Dlatego jeżeli nie jesteśmy pewni tego w jakim kierunku pójdziemy to zaczynamy skromnie i rozbudowujemy projekt/dodajemy dodatkowe warstwy abstrakcji wraz z czasem -> architektura ewolucyjna lub zwinna, po angielsku Agile Architecture.

@slsy

Dobre abstrakcje to nie tylko lepsza czytelność ale też większe możliwości.

Chyba gubimy wątek i sens dyskusji bo nigdzie nie napisałem, że dobre abstrakcje są złe. Istotą moich postów jest to aby używać dodatkowych warstw abstrakcji tam gdzie rzeczywiście mają sens, a nie robić z tego sztukę dla sztuki i komplikować rozwiązanie już od samego początku bo może za 3 lata być może będę musiał zmienić ORM-a albo bazę danych.

Zgoda? A jak nie to od razu piszmy wszystko jako mikroserwisy każdy z osobną bazą danych, na 6 warstwach i oczywiście odpalmy to na Kubernetesie w AWS z obowiązkowym Istio jako service mesh, bo może za 3 lata osiągniemy skalę Netflixa i dzięki temu nie będziemy musieli przepisywać aplikacji, tylko wyskalujemy klaster w górę i YOLO :D

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

Dobrze, to jaką chcesz przykładową apke? Co ma robić i ile ma mieć modeli, 10, 20, 30? Napisze Ci ją w odpowiedni sposób, udostępnię na publiczne repo i wtedy sam możesz stwierdzić czy to jest warte czy nie warte? Jakich funkcjonalności z EF ma użyć, żeby nie można było powiedzieć że to trywialny projekt? Myślę że to rozstrzygnie spór.

Wiem też, że aby coś zrobić dobrze potrzebne są nie tylko umiejętności ale i czas za który ktoś musi zapłacić..

No ja twierdzę inaczej.

Jesli popełniasz popularne błędy, albo masz wyuczone pewne złe schematy (a prawie każdy ma, i bardzo trudno się ich pozbyć), to dodaje się do tego dodatkowy czas jak mówisz - ale jeśli nie popełniasz tych błędów, i umiesz bardzo dobrze odseparować elementy od siebie, to nie musi to trwać dodatkowo dłużej. Może trwać tyle samo, jeśli tylko masz odpowiednie umiejętności i podejście.

1

@Riddle: Nie bierzesz czynnika ludzkiego pod uwagę. To że ty umiesz nie oznacza, że kolega obok umie a tym bardziej nie oznacza, że w ograniczym czasie znajdziesz kogoś na rynku kto umie.
Więc w czas wytworzenia projektu musisz doliczyć czas na naukę. Plus pomnożyć to przez wielkość rotacji w firmie/projekcie.

Każda dodatkowa linijka kodu to koszt.

1
Schadoow napisał(a):

@Riddle: Nie bierzesz czynnika ludzkiego pod uwagę. To że ty umiesz nie oznacza, że kolega obok umie a tym bardziej nie oznacza, że w ograniczym czasie znajdziesz kogoś na rynku kto umie.
Więc w czas wytworzenia projektu musisz doliczyć czas na naukę. Plus pomnożyć to przez wielkość rotacji w firmie/projekcie.

Każda dodatkowa linijka kodu to koszt.

Tylko zobacz jak się teraz zmienia narracja rozmowy :D

  • Najpierw była nie da się odseparować tych rzeczy
  • Potem było może się i da, ale to za długo trwa
  • Następnie może i nie trwa długo, ale za to inni ludzie w zespole tego nie skumają

:>

A odpowiedzią na to jest oczywiście pair programming. I nie ma problemu. Tylko że żeby to praktykować to trzeba wyjśc ze swojej strefy komfortu, bo z tego co się orientuje to 90% programistów nigdy nie praktyowała PP. Więc nie ma za bardzo o czym mówić.

3

nie nic się nie zmienia.
Architekturę wybierasz pod zespół a nie na odwrót. Nie da się bo takie zespoly które to ogarną to rzadkość i ryzyka zafiksowania się na dostawce. Nawet jak już jakimś cudem znajdzie się magik co to ogarnie w zespole to będzie długie i drogie bo trzeba resztę zespołu „nauczyć”. Ostatnie jest podsumowanie.

Rozumiem ze z własnej kieszeni pokryjesz ten pp ? Bo jak nie to musisz na starcie końcowa estymate mnożyć raz 2-6 w zależności od wielkości zespołu. Przez co twoja oferta jest niekonkurencyjna ergo firma nie dostaje projektu.

Dodatkowo weź po uwagę ze architektura to tylko jeden ze skili. I to ten w sumie najmniej istotny pod katem czy system będzie „działał”.

0
Schadoow napisał(a):

nie nic się nie zmienia.
Architekturę wybierasz pod zespół a nie na odwrót. Nie da się bo takie zespoly które to ogarną to rzadkość i ryzyka zafiksowania się na dostawce. Nawet jak już jakimś cudem znajdzie się magik co to ogarnie w zespole to będzie długie i drogie bo trzeba resztę zespołu „nauczyć”. Ostatnie jest podsumowanie.

Brzmi jakbyś kiedyś próbował zrobić coś takiego, nie wyszło, sparzyłeś się, i teraz jesteś uprzedzony.

Schadoow napisał(a):

Rozumiem ze z własnej kieszeni pokryjesz ten pp ? Bo jak nie to musisz na starcie końcowa estymate mnożyć raz 2-6 w zależności od wielkości zespołu. Przez co twoja oferta jest niekonkurencyjna ergo firma nie dostaje projektu.

mnożyć raz 2-6 :D Teraz to jestem na 100% przekonany że nigdy nie praktykowałeś PP.

1

@TTA120: Poruszyłeś dwa ciekawe wątki więc odpowiem w poście

Ale te problemy się łącza. Jeśli nie mam jakiejś warstwy pomiedzy biznezem a perststencja (choćby konfiguracja EFCore jak minimum, ukrycie publicznych seterów) to zostajemy z encjami których pola publiczne ustawiamy w jakiś kontrolerach czy serwisach.

Masz rację, ale popatrz na możliwości jakie daje ci EF Core, przykład:

public class Order
{
  private readonly List<OrderItem> _items = new();

  private readonly DateTime _createdOn;
  private readonly int _buyerId;
  
  private decimal _totalValue;
  
  public int Id { get; }
  public IReadOnlyCollection<OrderItem> Items => _items;

  public Order(int buyerId)
  {
    _buyerId = buyerId;
    _createdOn = DateTime.UtcNow;
  }

  public void AddItem(IEnumerable<OrderItem> items)
  {
    // fejkowa logika na szybko
    if (items.Count > 10)
      throw new OrderItemException("Too many items");
    
    _items.AddRange(items);
    
    Recalculate();
  }

  private decimal Recalculate()
  {
    // przeliczanie zamówienia i przypisanie do pola _totalValue;
  }
}

Przykład z czapy i na szybko ale pokazuje, że encje domenowe mapowane na tabelki przez EF Core mogą posiadać prywatne pola, settery i ładnie enkapsulować logikę. Nic nie musi wyciekać do serwisów czy kontrolerów. Taką encję przekazujemy do DbContextu w serwisie aplikacyjnym i gotowe. Nie trzeba żadnego repozytorium bo DbContext jest repozytorium i Unit of Work.

public class OrderService
{
  private readonly DbContext _dbContext;
  
  public Result CreateOrder(CreateOrder command)
  {
    var order = Order.New(command.UserId);
    order.AddItems(command.Items.FromDto());
    
    _dbContext.Orders.Add(order);
    _dbContext.SaveChanges();

    // zwróć rezulatat
  }
}

Oczywiście nie twierdzę że trzeba robić dodatkową warstwę między ORM a persystencją ale miedzy biznesem a persystencją to jakieś racjonalne minimu

Czasem tak, czasem nie. Dlaczego nie? Przykład powyżej. Jeżeli w przykładzie powyżej logika wychodzi/nie pasuje do encji to można wprowadzić pojęcie serwisu domenowego.

Kiedy tak? Kiedy robisz DDD i masz prawdziwe agregaty, wtedy stosujesz wzorzec Repository znany z DDD. Szczegóły implementacyjne można znaleźć w książkach Vernona i Evansa

0
markone_dev napisał(a):

Przykład z czapy i na szybko ale pokazuje, że encje domenowe mapowane na tabelki przez EF Core mogą posiadać prywatne pola, settery i ładnie enkapsulować logikę. Nic nie musi wyciekać do serwisów czy kontrolerów. Taką encję przekazujemy do DbContextu w serwisie aplikacyjnym i gotowe. Nie trzeba żadnego repozytorium bo DbContext jest repozytorium i Unit of Work.

I tutaj chyba jest pierwszy z niepoprawnych schematów. Bo to, że pola i wszystkie rzeczy których używasz są private, nie oznacza jeszcze że są zaenkapsulowane.

I sam Twój przykład pokazał że nie, bo przecież EF do nich wsadza wartości. To znaczy że owszem - Twój kod bezpośrednio nie ma do nich wjazdu (bo język to wymusza), ale EF już ma tam wjazd. Ergo ta klasa nie jest zanekapsulowana, bo ORM ustawia te wartości. Powiesz pewnie "co za różnica", a różnica jest taka że gdyby w istocie ten przykład który podałeś respektował enkapsulację to mógłbyś podmienić frameworki ORM' na jakie chcesz - ale nie jest tak, nie możesz, bo ten przykład ją łamie. Ale jesteś oszukany przez EF, bo widzisz private przy polach więc myślisz "no to musi być enkapsulacja, jak nie jak tak". Z tego wyciągasz potem błędny wniosek, że zbudowanie poprawnej abstrakcji jest nie możliwe bo musiałbyś się pozbyć tych private.

Poprawna interpretacja tego kodu który pokazałeś wygląda tak:

  • Jeśli Twój projekt składałby się z repozytoriów i innych obiektowych rozwiązań, byłby odpowiednio zankapsulowany i odpornoy na zmiany poza nim
  • EF Core łamie enkapsulację obietów, żeby działać, ale trochę oszukuję programistów po pozwala żeby te pola były private, przez co daje złudne poczucie tego że one są zaenkapsulowane
  • Ponieważ EF Core łamie enkapsulacje swoich modeli, to nie da się go łatwo podmienić na innego ORM'a (właśnie przez to że EF pisze po polach prywatnych a inne ORM'y nie)
  • Ergo jeśli ktoś chciałby zrobić krok w stronę lepszej enkapsulacji, to paradoksalnie powinien odejść od tych private na polach.

No bo co znaczy "enkapsulacja" tak na prawdę - to że możesz zmienić internalsy, tak że użytkownicy tego czegoś nadal działają. W przykładzie w którym podałeś, nie możesz, właśnie przez to że używasz EF. Więc nie ma tam enkapsulacji, mimo tego że masz private.

2

@Riddle:
Nie odpuścisz, co nie? :D

To znaczy że owszem - Twój kod bezpośrednio nie ma do nich wjazdu (bo język to wymusza), ale EF już ma tam wjazd.

Wiem i jestem tego świadom, bo to na czym mi najbardziej zależy to enkapsulacja logiki biznesowej. To że EF ma tam wjazd mnie bardzo cieszy bo robi za mnie trudną robotę ustawiania wartości pól i referencji w trakcie pobierania obiektu z bazy.

Powiesz pewnie "co za różnica", a różnica jest taka że gdyby w istocie ten przykład który podałeś respektował enkapsulację to mógłbyś podmienić frameworki ORM' na jakie chcesz

Nie powiem, bo na przestrzeni 12 lat miałem tylko jeden projekt w którym zmienialiśmy ORM-a (wspominałem o nim wyżej) i zwykle nie chcę zmieniać ORM-a. Jak zmieniam bazę danych z SQL Server na Postgres to robię to przy pomocy konfiguracji EF Core'a. A jak zmieniam na Mongo to i tak to jest na tyle duża zmiana że różnicy nie robi.

EF Core czy NHibernate mają wszystko co potrzebuję do wykonania 95% zadań. Pozostałe 5% da się zrobić SQL-em lub w inny sposób jak choćby przeliczając dane w tle osobną usługą i zamiast robić złożone i czasochłonne zapytania z aplikacji zrobić jeden select do tabeli gdzie te dane już są przetworzone.

Ale jesteś oszukany przez EF, bo widzisz private przy polach więc myślisz "no to musi być enkapsulacja, jak nie jak tak"

Nikt mnie nie oszukał bo jak wyżej ja chronię logikę biznesową i wiem jakie brzydkie rzeczy robi EF z moją encją domenową.

6

@Riddle: Od języka programowania też będziesz sie enkapsulował aby można go było podmienić xD ? Piszesz flow w takim xml'u i potem tylko generatory xD

0
markone_dev napisał(a):

@Riddle:
Nie odpuścisz, co nie? :D

To znaczy że owszem - Twój kod bezpośrednio nie ma do nich wjazdu (bo język to wymusza), ale EF już ma tam wjazd.

Wiem i jestem tego świadom, bo to na czym mi najbardziej zależy to enkapsulacja logiki biznesowej. To że EF ma tam wjazd mnie bardzo cieszy bo robi za mnie trudną robotę ustawiania wartości pól i referencji w trakcie pobierania obiektu z bazy.

Powiesz pewnie "co za różnica", a różnica jest taka że gdyby w istocie ten przykład który podałeś respektował enkapsulację to mógłbyś podmienić frameworki ORM' na jakie chcesz

Nie powiem, bo na przestrzeni 12 lat miałem tylko jeden projekt w którym zmienialiśmy ORM-a (wspominałem o nim wyżej) i zwykle nie chcę zmieniać ORM-a. Jak zmieniam bazę danych z SQL Server na Postgres to robię to przy pomocy konfiguracji EF Core'a. A jak zmieniam na Mongo to i tak to jest na tyle duża zmiana że różnicy nie robi.

EF Core czy NHibernate mają wszystko co potrzebuję do wykonania 95% zadań. Pozostałe 5% da się zrobić SQL-em lub w inny sposób jak choćby przeliczając dane w tle osobną usługą i zamiast robić złożone i czasochłonne zapytania z aplikacji zrobić jeden select do tabeli gdzie te dane już są przetworzone.

Ale jesteś oszukany przez EF, bo widzisz private przy polach więc myślisz "no to musi być enkapsulacja, jak nie jak tak"

Nikt mnie nie oszukał bo jak wyżej ja chronię logikę biznesową i wiem jakie brzydkie rzeczy robi EF z moją encją domenową.

No, więc to nie jest tak że "nie da się". Tylko, EF, jak wiele popularnych frameworków wsuwa swoje zwinne palce w każdy szczegół aplikacji, dodając bardzo ciasne (i często ukryte powiązania) przez co zbudowanie wysokiej jakości kodu jest przez to trudniejsze. Czasem żeby musisz zrobić krok wstecz, żeby móc zrobić dwa kroki w przód.

Wychodzi na to że takie podejście do modeli z EF bardzo się kłóci z odseparowaniem ORM'a od modeli. Więc pytanie się sprowadza do tego co kto woli:

  • Jeśli bardziej lubisz private i magię EF'a, to zostań przy tym, i zaakceptuj że framework przeszkadza w odpowiednim odseparowaniu warstw
  • Jeśli bardziej lubisz dependency inversion, to odejdź od magii EF'a i jawnie sprecyzuj zależności i odpowiednio dodaj enkapsulację (ale taką prawdziwą, gdzie nic nie miesza po privateach).

I w sumie to które wybierzesz to już jest preferencja. Ja wybiorę to drugie, bo pozwala to trzymać frameworki at arms length.

Schadoow napisał(a):

@Riddle: Od języka programowania też będziesz sie enkapsulował aby można go było podmienić xD ?

Nie.

Poza tym, powtarzałem już milion razy i powtórzę jeszcze raz. Podmienienie czegoś, to nie jest jedyna zaleta odpowiedniego odseparowania warstw - jest ich bardzo dużo. Podmianka całości to jest jakby tylko jedna z zalet.

0

@Riddle: No dobra, ale czy nie o to w tym chodzi że mamy jakis obiekt biznesowy który ma swój stan. Wszystkie dostępne działania możemy wykonać na nim w pamięci. Niestety rzadko zdarza się że użytkownik wszystko zrobi na przestrzeni jednej interakcji z systemem więc niestety musimy zapisać gdzieś taki obiekt (pech chciał że najczęściej w relacyjny SQL czy innym nieperfekcyjnym rozwiązaniu). Ale my tak naprawdę odtwarzamy tylko ten stan obiektu. Obiekt jest w poprawnym stanie bo zadbaliśmy o to w logice biznesowej, w momencie wykonywania zapisu mamy spójne dane, więc w momencie odczytu też mamy spójne dane i możemy wykonywać kolejne operacje na naszym spójnym obiekcie. Nie interesuje nas tu za bardzo że EF stworzył sobie ten obiekt refleksją i wtłoczył nam te informacje w prywatne pola tą refleksją. Jest to dla nas blackbox, warstwa abstrakcji która nas nie obchodzi a właśnie nam pomaga. Tak działa serializacja i deserializacja danych. Ja po 4 stronach tego wątku dalej nie wiem co proponujesz ale spróbuję zgadnąć i powiedz mi czy mam rację. Chcesz stworzyć klasę ktora będzie wykorzystywany przez ORM z publicznymi geterami i seterami oraz obiekt biznesowy który w konstruktorze przyjmie ten obiekt ORM owy i ustawi sobie w metodzie incijalizujacej poprawny stan tak? Wtedy teoretycznie nie potrzeba nam żadnej refleksji do stworzenia obiektów biznesowych w poprawnym stanie ale kosztem ręcznego mapowania (bo przypominam że mapery to też refleksja). Ale uwaga uwaga, w takim wypadku bibliotek biznesowa ma zależność do biblioteki która definiuje obiekty ORMa i wie o EF i np. używa jego adnotacji bo nasz klasa biznesowa musi przecież wiedzieć o klasie z której nastąpi mapowanie. Jest jeszcze druga opcja że proponujesz mapowanie z jednego na drugie w jakimś factory no ale wtedy sorry bo mamy klasy biznesowe które muszą mieć PUBLICZNE setery żeby dało się je zmapowac więc możemy utworzyć obiekt biznesowy w niepoprawnym stanie co dalej kończy się porażka enkapsulacji.

0

Ewentualnie jeszcze możemy mieć active record, czyli obiekt biznesowy sam (najczęściej poprzez dziedziczenie z klasy bazowej) odpowiada za swój zapis i odczyt, pełna enkapsulacja ale pogrzebanie SRP i chyba odeszliśmy od tego gdzieś w latach 2005.

0
TTA120 napisał(a):

@Riddle: No dobra, ale czy nie o to w tym chodzi że mamy jakis obiekt biznesowy który ma swój stan. Wszystkie dostępne działania możemy wykonać na nim w pamięci. Niestety rzadko zdarza się że użytkownik wszystko zrobi na przestrzeni jednej interakcji z systemem więc niestety musimy zapisać gdzieś taki obiekt (pech chciał że najczęściej w relacyjny SQL czy innym nieperfekcyjnym rozwiązaniu). Ale my tak naprawdę odtwarzamy tylko ten stan obiektu. Obiekt jest w poprawnym stanie bo zadbaliśmy o to w logice biznesowej, w momencie wykonywania zapisu mamy spójne dane, więc w momencie odczytu też mamy spójne dane i możemy wykonywać kolejne operacje na naszym spójnym obiekcie. Nie interesuje nas tu za bardzo że EF stworzył sobie ten obiekt refleksją i wtłoczył nam te informacje w prywatne pola tą refleksją. Jest to dla nas blackbox, warstwa abstrakcji która nas nie obchodzi a właśnie nam pomaga. Tak działa serializacja i deserializacja danych.

Ostatecznym testem tego czy coś jest zaenkapsulowane czy nie jest spróbować podmienić framework na inny. Jeśli się nie da, to po prostu to nie jest zaenkapsulowane - z definicji.

Ja po 4 stronach tego wątku dalej nie wiem co proponujesz ale spróbuję zgadnąć i powiedz mi czy mam rację. Chcesz stworzyć klasę ktora będzie wykorzystywany przez ORM z publicznymi geterami i seterami oraz obiekt biznesowy który w konstruktorze przyjmie ten obiekt ORM owy i ustawi sobie w metodzie incijalizujacej poprawny stan tak? Wtedy teoretycznie nie potrzeba nam żadnej refleksji do stworzenia obiektów biznesowych w poprawnym stanie ale kosztem ręcznego mapowania (bo przypominam że mapery to też refleksja). Ale uwaga uwaga, w takim wypadku bibliotek biznesowa ma zależność do biblioteki która definiuje obiekty ORMa i wie o EF i np. używa jego adnotacji bo nasz klasa biznesowa musi przecież wiedzieć o klasie z której nastąpi mapowanie. Jest jeszcze druga opcja że proponujesz mapowanie z jednego na drugie w jakimś factory no ale wtedy sorry bo mamy klasy biznesowe które muszą mieć PUBLICZNE setery żeby dało się je zmapowac więc możemy utworzyć obiekt biznesowy w niepoprawnym stanie co dalej kończy się porażka enkapsulacji.

No, nie jest to do czego dążę.

Dążę do tego, żeby część biznesowa aplikacji była niezależna od persystencji. Tylko że bazy relacyjne mają to do siebie, że dodają masę pracy do tego żeby móc jej używać (sqle, indexy, migracje, where'y nie where'y, mapowania, etc.). Ergo, więc ktoś podstawiony przed takim wyborem, ma dwa wyjścia: albo to ogarnąć samemu, albo zcouple'ować framework do swojej domeny. Ogarnianie tego samemu jest trudne - ale nie przez odseparowanie warstw, tylko przez to że praca z relacyjnymi bazami to ból. Natomiast użycie frameworka ma to do siebie że dodaje tight-coupling do naszego kodu.

Po prostu zapisanie stanu obiektów powinno być proste, w dobie nowej technologii.

3

Nie czytałem całego wątku, odpowiem tylko na pytanie zadane w temacie: Nie.

Jeśli uważasz, że Twój pomysł jest lepszy, wyjaśnij dlaczego (argumenty).
Jeśli to nie pomaga, znajdź mediatora, kogoś kto obiektywnie oceni kto ma rację.

Umiej się przyznać do błędu i uznać czyjąś rację.
Bądźcie pragmatyczni, popełniajcie błędy, wyciągajcie wnioski.

W ten sposób, pracuje się o wiele lepiej.
Nie traktuj kogoś starszego stopniem/doświadczeniem bezkrytcznie za wyznacznik wiedzy.

0

@TTA120:

Ewentualnie jeszcze możemy mieć active record, czyli obiekt biznesowy sam (najczęściej poprzez dziedziczenie z klasy bazowej) odpowiada za swój zapis i odczyt, pełna enkapsulacja ale pogrzebanie SRP i chyba odeszliśmy od tego gdzieś w latach 2005.

W Ruby ten pattern nadal jest zywy ;)

@Riddle

Jak czytam takie posty jak wyżej to zastanawiam się jaki argument ktoś miałby postawić, żeby kogoś przekonać że jeśli się zrobi odpowiednie odseparowanie logiki od persystencji, to praktycznie wszystko o czym mówicie jest szczegółem implementacyjnym.

Czy gdy zrobi się to odpowiednio, to nadal będzie wydajnie?

Dobrze, to jaką chcesz przykładową apke? Co ma robić i ile ma mieć modeli, 10, 20, 30? Napisze Ci ją w odpowiedni sposób, udostępnię na publiczne repo i wtedy sam możesz stwierdzić czy to jest warte czy nie warte? Jakich funkcjonalności z EF ma użyć, żeby nie można było powiedzieć że to trywialny projekt? Myślę że to rozstrzygnie spór.

Czekaj - dobrze rozumiem

Chcesz napisać warstwę nad dostępem do danych która np. używa EF Core + Postgresa i wystawić publiczne API tej warstwy jako np. IEnumerable/Expressions/Funcs/whatever

a później pod spodem przejść na np. Neo4j?

I oczywiście nie będzie to lipne wydajnościowo (cpu/bandwidth) przez fakt bycia zbyt ogólną abstrakcją?

To pokaż, ja chętnie zobaczę :)

3

Też podpinam się do prośby o jakiś kod, bo na chwilę obecną mam flashbacki rodem z konferencji javawowych na yt albo innych Legacy Fighterów. Wywód akademicko fajny, a w sumie to można robić wszystko i nic, bo to zależy, a od czego, to, cytując klasyka "po tym się weryfikuje konsultanta, co odpowie".

1

Dla mnie założenie że w projekcie ściśle wiążemy się z jakimś ORMem czy konkretną bazą (np. SQL server czy Postgres) i wykorzystamy np. Konkretne rodzaje zapytań czy funkcji dostępnych dla danej bazy jest jak najbardziej ok. Jak mam np. Rekurencyjne CTE w sql serverze i mogę je wykorzystać jak problem tego wymaga to czemu nie. Albo jak mogę napisać własne dużo bardziej wydajne i ostatecznie mniej skomplikowane niż dziwne hacki na expressionach to czemu nie - jestem pragmatykiem, nie będę zakładał że potrzebuje abstrakcji bo mogę przejść na no sql za 5 lat i co ja zrobię z takimi zapytaniami w kodzie. Siadam i piszę tego sql w warstwie infrastruktury a potem idę na kawe. Zmiana providera DB jest tak rzadkim wymaganiem że dużo bardziej spodziewam się modyfikacji w logice biznesowej i na to kładę nacisk, tam staram się dostosować abstrakcję żeby zmniejszyć koszt przyszłych zmian. Klient na pewno zarząda kiedyś zmiany w tej logice a zmiany db może nie chcieć nigdy albo za 10 lat, warto mieć oczywiście testy integracyjne które umożliwią taką zmianę, ale wciąż zakładać że będzie to zmiana kosztowna. Zmiana ORM to jeszcze większa abstrakcja, no chyba że, przyznajmy że nasz pierwotny wybór był słaby ... Oczywiście są czasami jakieś rodzaje oprogramowania które wymagają obsługi kilku baz bo są sprzedawane taśmowo szerokiej rzeszy klientów i marketing chcę dać im możliwość wyboru. Wtedy jesteśmy zmuszeni zapewnić jakaś abstrakcję żeby nie pisać w kodzie co chwila if (SelectedDbType=="mysql"), ale to są również skrajne przypadki i zwykle wiemy o tym w początkowych fazach projektu (mi się jeszcze nigdy to nie zdarzyło). Pisząc to wszystko czekam na przykład od @Riddle bo może stwierdzę, że faktycznie "no o tym to nigdy nie pomyślałem".

0
TTA120 napisał(a):

Dla mnie założenie że w projekcie ściśle wiążemy się z jakimś ORMem czy konkretną bazą (np. SQL server czy Postgres) i wykorzystamy np. Konkretne rodzaje zapytań czy funkcji dostępnych dla danej bazy jest jak najbardziej ok. Jak mam np. Rekurencyjne CTE w sql serverze i mogę je wykorzystać jak problem tego wymaga to czemu nie. Albo jak mogę napisać własne dużo bardziej wydajne i ostatecznie mniej skomplikowane niż dziwne hacki na expressionach to czemu nie - jestem pragmatykiem, nie będę zakładał że potrzebuje abstrakcji bo mogę przejść na no sql za 5 lat i co ja zrobię z takimi zapytaniami w kodzie. Siadam i piszę tego sql w warstwie infrastruktury a potem idę na kawe. Zmiana providera DB jest tak rzadkim wymaganiem że dużo bardziej spodziewam się modyfikacji w logice biznesowej i na to kładę nacisk, tam staram się dostosować abstrakcję żeby zmniejszyć koszt przyszłych zmian. Klient na pewno zarząda kiedyś zmiany w tej logice a zmiany db może nie chcieć nigdy albo za 10 lat, warto mieć oczywiście testy integracyjne które umożliwią taką zmianę, ale wciąż zakładać że będzie to zmiana kosztowna. Zmiana ORM to jeszcze większa abstrakcja, no chyba że, przyznajmy że nasz pierwotny wybór był słaby ... Oczywiście są czasami jakieś rodzaje oprogramowania które wymagają obsługi kilku baz bo są sprzedawane taśmowo szerokiej rzeszy klientów i marketing chcę dać im możliwość wyboru. Wtedy jesteśmy zmuszeni zapewnić jakaś abstrakcję żeby nie pisać w kodzie co chwila if (SelectedDbType=="mysql"), ale to są również skrajne przypadki i zwykle wiemy o tym w początkowych fazach projektu (mi się jeszcze nigdy to nie zdarzyło). Pisząc to wszystko czekam na przykład od @Riddle bo może stwierdzę, że faktycznie "no o tym to nigdy nie pomyślałem".

Zaczynam powoli podejrzewać że podejście które stosuje w pracy jest chyba zbyt różne od tego co jest popularne tutaj wśród wpisach, i nie jestem w stanie go skutecznie opowiedzieć w jednym czy nawet kilku postach na forum :/

2

@Riddle

Wystarczy aby za słowami szły czyny i abyś napisał jakiś kodzik

PS: Przy okazji jak już to będziesz implementował, to kiedyś obiecałeś że wrzucisz video jak wygląda twój workflow TDD i odpalaniem testów bez przerwy, co kilka dodanych literek w kodzie.

Testowanie - na jakim poziomie najwięcej testów?

3
Riddle napisał(a):

praca z relacyjnymi bazami to ból.

Widać, że nie pracowałeś z prawdziwymi relacyjnami bazami. Mam wrażenie, że kiedyś spróbowałes i się sparzyłeś. Dobrze zastosowane relacyjne bazy danych działają idealnie i zwiększają efektywność około 2-3 razy z mojego doświadczenia

1
WeiXiao napisał(a):

@Riddle

Wystarczy aby za słowami szły czyny i abyś napisał jakiś kodzik

PS: Przy okazji jak już to będziesz implementował, to kiedyś obiecałeś że wrzucisz video jak wygląda twój workflow TDD i odpalaniem testów bez przerwy, co kilka dodanych literek w kodzie.

Testowanie - na jakim poziomie najwięcej testów?

Proponuję również dorzucić kogoś do PP do dostaniemy to dwa razy szybciej

1
markone_dev napisał(a):

Micro ORM ale nie chodzi tu o semantykę, tylko zmianę technologii używanej do persystencji modelu domenowego. Problem Dappera jest taki, że nie lubi się bardzo z prywatnymi polami i setterami. W przypadku, gdy tak jak piszesz masz bogatą domenę z wieloma typami a nie tylko proste stringi i inty to zaczyna się czarowanie żeby to ręcznie poskładać.

Nie o semantykę chodzi, tylko jeśli coś z definicji nie nadaje się do czegoś, to trudno wymagać, aby mogło zastępować coś, co jest od tego czegoś lepsze.

Wystarczy sobie popatrzeć jak obsługiwana jest współbierzność w NHibernate i EF Core, żeby zobaczyć, że zmiana z jednego na drugi wymaga zmiany typu pola na modelu. Do tego EF Core wciąż ma problem z inicjowaniem prywatnych backing fields jak kolekcje przy włączonym lazy loadingu i potem żeby coś sprawdzić na kolekcji (jakiś niezmiennik) robić hacki w postaci items.Count żeby zainicjować kolekcję. Jak się pisze TODO listę to faktycznie zmiana z A na B będzie trywialna, ale przy złożonych domenach, gdzie chcemy enkapsulować logikę na poziomie modelu już nie będzie tak prosto żeby zmienić A na B

Czyli EF się nie nadaje do mapowania obiektowej domeny.

Robienie takiej samej abstrakcji na swojej bazie danych w większości sytuacji nie ma sensu bo czas pracy włożony w utworzenie takiej ACL i jej utrzymywanie w czasie tylko dlatego że może za 3 lub 4 lata zmienimy SQL Server na Mongo albo Cassandre jest nieuzasadnionym kosztem. Ale to moje zdanie i nie trzeba się z nim zgadzać :P

W ogóle nie rozpatruję kwestii zmiany bazy danych ani ORMa, jedynie efektywne połączenie bogatej domeny z bazą nieobiektową.

Jeśli mamy obiektową, poprawnie enkapsulowaną domenę implementującą logikę biznesową, to mamy dwie możliwości:

  1. Walczymy z ORMem, aby ją zmapować, prawdopodobnie się nie udaje, więc trzeba brudzić kod domenowy tak, aby ORM go zaczął ogarniać. Do tego powstają jakieś pluginy/rozszerzenia/customizacje do ORMa mające pohackować to, czego jego twórcy nie przewidzieli. Na końcu i tak wyjeżdża Microsoft cały na czarno, ze stwierdzeniem, że liczby zmiennoprzecinkowe będą ucinane zamiast zaokrąglane do precyzji, bo to jest błąd, ale bardzo stary i dużo kodu od niego zależy, więc nie będą naprawiać.
  2. Dodajemy oddzielny model składowania, i repozytoria (które i tak mamy), mapują na ten model, który jest zaprojektowany tak, że ORM go rozumie z łatwością.

Sposób 2 jest banalny do implementacji, w praktyce (w rzeczywistym projekcie, nie przykładzie z bloga) może nawet będzie miał mniej kodu (bo jednolijkowe DTO dla repozytoriów zajmą raczej mniej kodu niż niż wszystkie dzikie obejścia i haki na konfigurację ORMa razem wzięte), a przede wszystkim zajmie mniej czasu, bo hackowanie to długotrwały proces w porównaniu do pisania prostych DTO.
Oczywiście powód do chwały mniejszy, bo każdy przecież chce być hakierem w świecie spaghetti.

Typowy przypadek w typowej polskiej plantacji spaghetti zwanej dla niepoznaki software house nie jest żadnym z powyższych, bo tam się nie stosuje ani metod w "encjach", za to ORMy są owrapowane co najmniej w repozytoria (bo i wrapper na ORM się zdarza, "na wszelki wypadek"), no i to wszystko to oczywiście strata czasu, ale o tym już pisałem wiele razy, i nie chcę więcej; nie jest to problem, który rozważam teraz.

TTA120 napisał(a):

No to chyba masz bardzo stare informacje. W EfCore można już obecnie wszystko skonfigurować zewnętrznie a nawet wiecej niż na atrybutach. Powiąznia n do n bez klasy pośredniej również działaja i to nawet by convetion, oczywiście można je dodatkowo skonfigurować. Nie musimy mieć nawet żadnych publicznych seterów.

To super, że mają już to, co konkurencja 15 lat temu.
Tylko ja tu raz czytam, że EFCore umie, raz że nie umie - pogubić się można w tych zmianach zeznań. :P

Schadoow napisał(a):

Architekturę wybierasz pod zespół a nie na odwrót. Nie da się bo takie zespoly które to ogarną to rzadkość i ryzyka zafiksowania się na dostawce. Nawet jak już jakimś cudem znajdzie się magik co to ogarnie w zespole to będzie długie i drogie bo trzeba resztę zespołu „nauczyć”. Ostatnie jest podsumowanie.

Skoro tak nam zależy na prostocie, to czemu zatem nie wybrać prostszego rozwiązania (nr 2 z mojej listy)?

A tak poza tym, to jeszcze nie spotkałem się z wybieraniem architektury pod zespół. Pod problem czasem tak, zazwyczaj pod organizację, ale jeszcze nigdy pod zespół.
Pod zespół mógłby wybrać co najwyżej sam zespół, ale nie jakiś ktoś z zewnątrz zespołu, kto przecież tego zespołu nie zna.

Rozumiem ze z własnej kieszeni pokryjesz ten pp ? Bo jak nie to musisz na starcie końcowa estymate mnożyć raz 2-6 w zależności od wielkości zespołu. Przez co twoja oferta jest niekonkurencyjna ergo firma nie dostaje projektu.

Nie tak działa pair programming. Ale jeśli chodzi o konkurowanie kosztami, to dyskusja nie dotyczy jakości, bo z niej się świadomie rezygnuje.

TTA120 napisał(a):

obiekt biznesowy który w konstruktorze przyjmie ten obiekt ORM owy i ustawi sobie w metodzie incijalizujacej poprawny stan tak?

To jest raczej słaby pomysł, niech konstruktor przyjmuje proste wartości (nie chodzi o to, że tylko string i int, po prostu nie typy z zewnętrznych warstw).

Jest jeszcze druga opcja że proponujesz mapowanie z jednego na drugie w jakimś factory no ale wtedy sorry bo mamy klasy biznesowe które muszą mieć PUBLICZNE setery żeby dało się je zmapowac więc możemy utworzyć obiekt biznesowy w niepoprawnym stanie co dalej kończy się porażka enkapsulacji.

Można użyć bibliotek, które nie mają takich perwersyjnych ograniczeń w mapowaniu.

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.