Czy tworzyć serwisy, jeśli repozytorium pokrywa wszystkie akcje?

Czy tworzyć serwisy, jeśli repozytorium pokrywa wszystkie akcje?
bbhzp
  • Rejestracja:około rok
  • Ostatnio:około 2 godziny
  • Postów:68
0

Cześć,
uczę się tworzyć aplikacje webowe (API w C#) - do tej pory tworzyłem tylko desktopy.

Powiedzmy, że mam AuthController z endpointami /login, /register i /refresh (dla tokenów JWT). Żeby nie umieszczać żadnej logiki w kontrolerze, stworzyłem AuthService z metodami Login(), Register() i Refresh(), które wstrzykuję za pomocą DI.

Ale co jeśli mam już stworzone repozytorium, które pokrywa wszystkie akcje kontrolera? Czy też powinienem tworzyć dla niego oddzielnie serwis?

Dla przykładu, chcę stworzyć CustomersController ze standardowymi metodami GET, POST, PUT i DELETE. Mam już utworzone CustomersRepository, które implementuje metody takie jak GetCustomers(), GetCustomerById(), AddCustomer(), itd. Metody te walidują model, ustawiają pola takie jak data dodania czy edycji, sprawdzają uprawnienia użytkownika (każdy użytkownik może mieć inne uprawnienia, więc pobieram je z bazy) i następnie wysyłają kwerendę do sql.

Czy w takim przypadku mogę do takiego kontrolera wstzyknąć samo repozytorium, czy może powinienem to jakoś rozbić na serwisy? (Nie chodzi mi o mikroserwisy, tworzę monolit)

Dodam jeszcze, że metody repozytorium nie zawierają całego kodu - walidację przeprowadzam w oddzielnej klasie, np. CustomersValidator, a uprawnienia sprawdzam z pomocą np. PrivilegesRepository.GetPrivilege().

Z góry bardzo dziękuję.

edytowany 1x, ostatnio: Riddle
RJ
  • Rejestracja:ponad 2 lata
  • Ostatnio:około godziny
  • Postów:429
2

Jeśli nie masz potrzeby operowania w pamięci i sterowania przepływem to chyba jest ok.

Zawsze można później ten serwis dodać, jeśli zajdzie potrzeba.

Pragmatycznie 😉

bbhzp
  • Rejestracja:około rok
  • Ostatnio:około 2 godziny
  • Postów:68
0
rjakubowski napisał(a):

Jeśli nie masz potrzeby operowania w pamięci i sterowania przepływem to chyba jest ok.

Zawsze można później ten serwis dodać, jeśli zajdzie potrzeba.

Pragmatycznie 😉

Dziękuję :)

A kiedy by w zasadzie zaszła taka potrzeba, tzn. kiedy samo repozytorium nie wystarczy? Czytałem różne wpisy (chociażby Services vs. Repository - różnice), ale nie do końca rozumiem tego rozgraniczenia. No chyba, że repozytorium to klasa, która korzysta z bazy, a serwis to np. właśnie taki AuthService, który generuje tokeny JWT?

M0
  • Rejestracja:ponad 11 lat
  • Ostatnio:minuta
  • Postów:362
4
bbhzp napisał(a):

A kiedy by w zasadzie zaszła taka potrzeba, tzn. kiedy samo repozytorium nie wystarczy? Czytałem różne wpisy (chociażby Services vs. Repository - różnice), ale nie do końca rozumiem tego rozgraniczenia. No chyba, że repozytorium to klasa, która korzysta z bazy, a serwis to np. właśnie taki AuthService, który generuje tokeny JWT?

Jak dla mnie repozytorium, to miejsce gdzie robisz tylko operację na bazie danych. W teorii, kiedy chcesz zmienić źródło danych, to podmieniasz implementację i powinno działać. Sprawdzenie uprawnień powinno być wykonane w innym miejscu(np. już na etapie endpointu w middleware).

sprawdzają uprawnienia użytkownika (każdy użytkownik może mieć inne uprawnienia, więc pobieram je z bazy)

Uważam, że jest to złe miejsce. Jak zapisujesz dane na bazie, to niech repozytorium zajmuje się tylko zapisem na bazie, a nie sprawdzeniem uprawnień. To powinno się robić wyżej, middleware czy chodźby serwis.

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około godziny
  • Lokalizacja:Laska, z Polski
  • Postów:10053
4
bbhzp napisał(a):

Ale co jeśli mam już stworzone repozytorium, które pokrywa wszystkie akcje kontrolera?

To znaczy że albo Twój program w tym miejscu nie ma logiki i figuruje jedynie jako warstwa do zapisu danych (trochę tak jak program notatnik nie ma swojej logiki tylko zapisuje swój input jako plik); albo oznacza to że przez przypadek włożyłeś swoją logikę do repozytorium.

bbhzp napisał(a):

Czy też powinienem tworzyć dla niego oddzielnie serwis?

Nie ma nic złego w tym żeby kontroler brał od razu repozytorium (pod warunkiem że sam kontroler nie ma logiki).

Jak będziesz chciał potem dodać serwis to będzie to bardzo łatwe do dodania.

Niektórzy mogą Ci doradzać żeby dodać teraz ten serwis mimo że teraz nie jest potrzebny, bo może się okazać że będzie potrzebny później - tylko takie osoby wychodzą z założenia że "lepiej dodać teraz, bo potem będzie trudniej", ale IMO to jest całkowicie złe podejście. Nie ma powodu czemu dodanie tej zmiany powinno być trudne w przyszłości.

edytowany 4x, ostatnio: Riddle
hzmzp
  • Rejestracja:ponad 11 lat
  • Ostatnio:5 minut
  • Postów:619
5
bbhzp napisał(a):

Ale co jeśli mam już stworzone repozytorium, które pokrywa wszystkie akcje kontrolera? Czy też powinienem tworzyć dla niego oddzielnie serwis?

Osobiście odradzam upychania repo do kontrolera. Uważam za dobrą praktykę ukrywania repozytorium za warstwą serwisu. Zwykle gdy program się rozrasta, to nagle okazuje się że serwis jest jednak potrzebny i albo robi się refactor albo zaczyna tworzyć kod spagetti.

HA
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 18 godzin
  • Postów:69
3
Riddle napisał(a):

Nie ma nic złego w tym żeby kontroler brał od razu repozytorium (pod warunkiem że sam kontroler nie ma logiki).

Ja bym poszedł krok dalej i powiedział, że jeśli logiki nie ma zbyt dużo i/lub nie jest do końca jasne gdzie ją upakować to jest w porządku napisać ją w kontrolerze. Trzeba tylko być czujnym i gdy ta logika zacznie się rozrastać lub powtarzać w więcej niż 2 miejscach w aplikacji, trzeba ją ładnie wydzielić - czy to do funkcji, czy do serwisu.

Szumnie nazywa się to Vertical Slice Architecture, w praktyce chodzi o przestrzeganie YANGI i KISS. Jeśli twoja logika mieści się w jednym czy dwóch prostych ifach to zazwyczaj nie ma sensu robić kolejnej warstwy tylko dla niej.

edytowany 1x, ostatnio: hawus
Miang
tylko ze wtedy to już nie powinno się na to mówić kontroler
superdurszlak
  • Rejestracja:prawie 7 lat
  • Ostatnio:4 dni
  • Lokalizacja:Kraków
  • Postów:1999
2

A celujesz w konkretną architekturę / strukturę aplikacji, czy po prostu chcesz zrobić jak najprościej?

Jeśli celujesz w porty i adaptery albo inne heksagony (to są bardzo podobne podejścia), to kontroler i implementacja repozytorium są adapterami, więc poniekąd pomijasz implantację w core. Możesz ten serwis dodać dla picu, jeśli nie masz logiki której nie powinieneś wyciągnąć z repozytorium, a możesz przyjrzeć się implementacji i pomyśleć czy na pewno wszystko co w nim jest jest zależne np. Od SQLa albo Redisa, albo co tam masz.

Powiązanie kontrolera bezpośrednio z repozytorium może być ryzykowne z uwagi na to, że z jednej strony masz modele związane z wystawiamym API, z drugiej ze storage pod spodem. Łatwo tutaj przeholować z chodzeniem na skróty i zrobić "encja na twarz i pchasz". Jak raz w to wdepniesz, to ciężko się od tego uwolnić, za to kończysz z kiepskim API, lub kiepską schemą, lub jednym i drugim, a w dodatku jeśli pojawi się jakaś logika aplikacji (zależy co robisz) to może się okazać, że nie ma jej gdzie dodać, nie ma czasu refaktorować, więc dodasz dziekolwiek.

Możesz też założyć, że serwis ma konkretną odpowiedzialność i jedną publiczną metodę, choć dla mnie to już był overkill i na tym etapie odpuściłem, bo te odpowiedzialności musiałyby być dosyć rozbudowane by to się opłaciło.


edytowany 1x, ostatnio: superdurszlak
RJ
  • Rejestracja:ponad 2 lata
  • Ostatnio:około godziny
  • Postów:429
1

@bbhzp: kiedy? Jak potrzebujesz logikę mieć jakąś na danych które skladujesz lub wyciągasz z bazy. Może jakiś side effect trzeba ogarnąć? Albo np. CSVke ciapnąć. Miliard możliwości.

SL
  • Rejestracja:około 7 lat
  • Ostatnio:6 minut
  • Postów:862
3

Zależy. Dodanie serwisu, gdy będzie potrzebny (pojawi się dodatkowa logika) jest badzo proste. Z drugiej strony dodanie go od samego początku nie jest trudne i może być dobrym lekiem na to, że ktoś będzie leniwy i doda logikę w repozytorium zamiast utworzyć nowy serwis

Najważniejsze to mieć testy na poziomie aplikacji, dzięki czemu taki refaktor jest prosty

bbhzp
  • Rejestracja:około rok
  • Ostatnio:około 2 godziny
  • Postów:68
0

Dziękuję wszystkim za odpowiedzi :)

superdurszlak napisał(a):

A celujesz w konkretną architekturę / strukturę aplikacji, czy po prostu chcesz zrobić jak najprościej?

Tak po środku, czyli po prostu dobrze :) Nie korzystam na razie na sztywno z jakiś konkretnych architektur, ale nie daję też wszystkiego do funkcji main().

Faktycznie, gdybym musiał nagle dopisać jakąś logikę do endpointa, to najpierw stworzyłbym ją bezpośrednio przed wywołaniem metody z repozytorium w kontrolerze, zamiast pisać cały nowy serwis i przerzucać wszystko do niego (... a jest to złe podejście). O podobnym problemie piszą slsy, hzmzp i inni.

hawus napisał(a):

Ja bym poszedł krok dalej i powiedział, że jeśli logiki nie ma zbyt dużo i/lub nie jest do końca jasne gdzie ją upakować to jest w porządku napisać ją w kontrolerze. Trzeba tylko być czujnym i gdy ta logika zacznie się rozrastać lub powtarzać w więcej niż 2 miejscach w aplikacji, trzeba ją ładnie wydzielić - czy to do funkcji, czy do serwisu.

Szumnie nazywa się to Vertical Slice Architecture, w praktyce chodzi o przestrzeganie YANGI i KISS. Jeśli twoja logika mieści się w jednym czy dwóch prostych ifach to zazwyczaj nie ma sensu robić kolejnej warstwy tylko dla niej.

Też tak uważam; gdybym miał endpoint, który zwraca np. bieżącą godzinę, to zrobiłbym po prostu:

Kopiuj
public IActionResult GetTime()
{
  return Ok(DateTime.Now);
}

zamiast tworzyć osobny TimeService, czy jakoś tak :)

rjakubowski napisał(a):

@bbhzp: kiedy? Jak potrzebujesz logikę mieć jakąś na danych które skladujesz lub wyciągasz z bazy. Może jakiś side effect trzeba ogarnąć? Albo np. CSVke ciapnąć. Miliard możliwości.

Prawda, no eksportu do CSV to bym w repozytorium nie umieścił, więc zostaje serwis :)

Michalk001 napisał(a):
bbhzp napisał(a):

sprawdzają uprawnienia użytkownika (każdy użytkownik może mieć inne uprawnienia, więc pobieram je z bazy)

Uważam, że jest to złe miejsce. Jak zapisujesz dane na bazie, to niech repozytorium zajmuje się tylko zapisem na bazie, a nie sprawdzeniem uprawnień. To powinno się robić wyżej, middleware czy chodźby serwis.

Masz rację, ale sprawdzając uprawnienia w samym repozytorium chciałem dać zabezpieczenie, aby ktoś inny, który nie pisze serwisu tylko bezpośrednio operuje na repozytoriach, nie wywołał przypadkiem niepożądanych zmian.

edytowany 3x, ostatnio: bbhzp
M0
  • Rejestracja:ponad 11 lat
  • Ostatnio:minuta
  • Postów:362
2
bbhzp napisał(a):

zamiast tworzyć osobny TimeService, czy jakoś tak :)

Hehe, to przed TimeProvider(chociaż nadal pewnie też), taką abstrakcję się tworzy, aby móc do testów robić moca czasu.

bbhzp
"W żadnym wypadku! A komu to potrzebne" :)
hzmzp
  • Rejestracja:ponad 11 lat
  • Ostatnio:5 minut
  • Postów:619
4
bbhzp napisał(a):
Kopiuj
public IActionResult GetTime()
{
  return Ok(DateTime.Now);
}

Na tego typu uproszczeniach to dopiero można zrobić poślizg! A zwłaszcza przy testach.
Jeżeli masz takie przemyślenia to bym sugerował w ciemno robić te serwisy nawet jak wydają ci się nie potrzebne.

edytowany 1x, ostatnio: hzmzp
HS
  • Rejestracja:10 miesięcy
  • Ostatnio:około 20 godzin
  • Postów:74
5
Riddle napisał(a):
bbhzp napisał(a):

Czy też powinienem tworzyć dla niego oddzielnie serwis?

Nie ma nic złego w tym żeby kontroler brał od razu repozytorium (pod warunkiem że sam kontroler nie ma logiki).

Pod warunkiem ze masz 100% pewnosc, ze cala reszta kodu bedzie funkcjonowala w identyczny sposob.
W rzeczywistosci bywa jednak bardzo "roznie" i wtedy w obliczu kazdej nowej sytuacji albo refaktorujesz wstecznie, albo tworzysz niejednolity, nieczytelny i rozmemlany strukturalnie kod.

Ustalenie od samego poczatku jedolitej struktury, np:

Controller(Responder(ActionService(Action(Repository))))

bardzo czesto na poczatku wydaje sie bezsensownym overkillem, jednak z czasem, w miare rozwoju applikacji, zaczynasz byc coraz bardziej sobie wdzieczny za utrzymanie dyscypliny przyjetej na starcie.

Riddle napisał(a):

Jak będziesz chciał potem dodać serwis to będzie to bardzo łatwe do dodania.

I robisz kociol w strukturze kodu, bo raz spinasz warstwe repo z kontrolerem, a innym razem z serwisem.
Pomijajac esetyke, zwieksza to np prog wejscia w kod.

Riddle napisał(a):

Niektórzy mogą Ci doradzać żeby dodać teraz ten serwis mimo że teraz nie jest potrzebny, bo może się okazać że będzie potrzebny później - tylko takie osoby wychodzą z założenia że "lepiej dodać teraz, bo potem będzie trudniej", ale IMO to jest całkowicie złe podejście. Nie ma powodu czemu dodanie tej zmiany powinno być trudne w przyszłości.

A niektorzy po prostu wychodza z zalozenia ze spojny strukturalnie kod jest o wiele latwiejszy w utrzymaniu, zwlasza, jesli w identyczny sposob tworzy sie nie jedna ale kilka aplikacji, ktore pozniej, w mniejszym lub wiekszym stopniu nalezy utrzymac.
W takim przypadku, zeby wejsc w kod takiej aplikacji, przynajmniej polowe wysilku poznawczego masz z glowy (kontekst strukturalny) i mozesz spokojnie skupic sie wylacznie na kontekscie biznesowym.

edytowany 1x, ostatnio: hyper-stack
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 13 godzin
  • Lokalizacja:Wrocław
3

To raczej repozytorium jest zbędne niż serwis.
Zwłaszcza repozytorium, które nie jest repozytorium (czyli abstrakcją na warstwę danych zachowującą się jak kolekcja danych danego rodzaju).

RJ
  • Rejestracja:ponad 2 lata
  • Ostatnio:około godziny
  • Postów:429
3

A jeszcze jak taka tu dyskusja odnośnie serwisów i repo. @bbhzp poczytaj sobie o MediatR i zobacz, jak ułatwia życie.

bbhzp
Faktycznie, wtedy w kontrolerze wywołujemy po prostu _mediator.Send :)
RJ
I nie szukasz w 1000 linii... Fakt, że nowoczesne IDE sobie dobrze radzą z wyszukiwaniem, ale jak masz konwencję Command i Handler i te cosie siedzą na jednym folderze to łatwiej jest nawigować i jakoś też tak bardziej po ludzku imo. Jak się widzi klasy 1k LoC to różne odczucia przychodzą 😉
M0
  • Rejestracja:ponad 11 lat
  • Ostatnio:minuta
  • Postów:362
1
rjakubowski napisał(a):

A jeszcze jak taka tu dyskusja odnośnie serwisów i repo. @bbhzp poczytaj sobie o MediatR i zobacz, jak ułatwia życie.

Potwierdzam, warto to stosować. Mamy pewien narzut rzeczy jakie musimy zrobić, ale w dłuższym okresie czasu łatwiej jest tym zarządzać. Plus nie potrzebujemy już pisać serwisów(brak serwisów, nie oznacza nie wydzielenia logiki to osobnych klas). Jak już tak polecamy, to też polecam language-ext(plus parę własnych metod rozszerzających)

HA
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 18 godzin
  • Postów:69
3
hyper-stack napisał(a):

Ustalenie od samego poczatku jedolitej struktury, np:

Controller(Responder(ActionService(Action(Repository))))

...kończy się "repozytoriami", które mają 20 metod typu GetByID, FindByName, FindByAuthorID, FindByAuthorName, FindByTags, FindByMoonPhase itd. i przepychaniem tych samych danych przez 5 warstw.

edytowany 1x, ostatnio: hawus
HS
  • Rejestracja:10 miesięcy
  • Ostatnio:około 20 godzin
  • Postów:74
1
hawus napisał(a):
hyper-stack napisał(a):

Ustalenie od samego poczatku jedolitej struktury, np:

Controller(Responder(ActionService(Action(Repository))))

...kończy się "repozytoriami", które mają 20 metod typu GetByID, FindByName, FindByAuthorID, FindByAuthorName, FindByTags, FindByMoonPhase itd. i przepychaniem tych samych danych przez 5 warstw.

Mylisz sie. W najgorszym wypadku konczy sie dziedziczeniem, a w optymalnym (php) traitem - oba z pojedynczymi zakresowymi metodami typu findBy(criteria), ale zeby miec jakiekolwiek pojecie o traitach trzeba mimo wszystko wyjsc ze znajomoscia php poza wersje 5.6 albo hejterskie shorty innych wylacznie prawilnych jezykow.

superdurszlak
co ma PHP 5.6 do tematu założonego przez OPa?
Miang
a masz łapkę za to PHP
HA
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 18 godzin
  • Postów:69
1
hyper-stack napisał(a):
hawus napisał(a):
hyper-stack napisał(a):

Ustalenie od samego poczatku jedolitej struktury, np:

Controller(Responder(ActionService(Action(Repository))))

...kończy się "repozytoriami", które mają 20 metod typu GetByID, FindByName, FindByAuthorID, FindByAuthorName, FindByTags, FindByMoonPhase itd. i przepychaniem tych samych danych przez 5 warstw.

Mylisz sie. W najgorszym wypadku konczy sie dziedziczeniem, a w optymalnym (php) traitem - oba z pojedynczymi zakresowymi metodami typu findBy(criteria), ale zeby miec jakiekolwiek pojecie o traitach trzeba mimo wszystko wyjsc ze znajomoscia php poza wersje 5.6 albo hejterskie shorty innych wylacznie prawilnych jezykow.

To działa tylko jak masz tylko jedną reprezentację, albo jak lubisz robić jeden model do reprezentowania wszystkich przypadków. No, albo jak lubisz re-implementować SQLa tymi criteriami. Ale czy nie łatwiej wtedy po prostu strzelić SQLem bezpośrednio do bazy?

A3
  • Rejestracja:5 miesięcy
  • Ostatnio:około 6 godzin
  • Postów:35
2

Prawda, no eksportu do CSV to bym w repozytorium nie umieścił, więc zostaje serwis 😀

To jest przykład na coś innego co bym uważał - robienie wielkich i potężnych "serwisów". O ile Repozytoria (choć często tak naprawdę to DAO a nie repozytoria w znaczeniu DDDD) i Controllery to są obiekty które mają mieć pewne okreslene znaczenie jako warstwa infrastruktury, to uważam określenie typu "serwis" za bardzo niejednoznaczne i opisujące zbiór metod nie wiadomo co robiących. Na pewno eskport do CSV/pliku etc trzymałbym gdzies osobno jako jakiś inny byt

HS
  • Rejestracja:10 miesięcy
  • Ostatnio:około 20 godzin
  • Postów:74
1
hawus napisał(a):

To działa tylko jak masz tylko jedną reprezentację,

Pojecia nie mam o czym Ty do mnie rozmawiasz.

hawus napisał(a):

albo jak lubisz robić jeden model do reprezentowania wszystkich przypadków.

NIe mam pojecia co w tym przypadku rozumiesz przez model. Jesli cos w rodzaju jakiegos uniwersalnego dto, encji albo dowolnego pojemnika, to z kolei Ty nie masz pojecia o czym ja mowie, natomiast jesli masz na mysli jakis uniwersalny flow postepowania, to tak, zgadza sie, lubie uogolniac przypadki tam gdzie to jest mozliwe.
Stad tez repozytorium jest dla mnie (i rowniez dla o wiele bardziej ogarnietych ode mnie) jedynie wrapperem wokol dowolnego data source klienta/manager'a, nie tylko bazodanowego.

hawus napisał(a):

No, albo jak lubisz re-implementować SQLa tymi criteriami.

Re-implementowac ..... SQL'a ...... criteria'mi ...... - nie jestem pewien czy dobrze rozumiem. Masz na mysli powtarzanie deklaracji kwerendy w kodzie? Jesli tak, to stanowczo nie mam tego na mysli. Kwerenda w sensie deklaracji jest jedna, a criteria ja po prostu dynamicznie adaptuja.

Ale czy nie łatwiej wtedy po prostu strzelić SQLem bezpośrednio do bazy?

A czymze jest repozytorium jesli nie giwera, ktora w taki czy inny sposob wlasnie do tej bazy (zreszta dowolnego data source) strzelasz?
Mozesz z wolnej reki (dbal) albo z podporka (orm), ale to jest caly czas repozytorium.

HS
  • Rejestracja:10 miesięcy
  • Ostatnio:około 20 godzin
  • Postów:74
0
Aleksander-32 napisał(a):

Prawda, no eksportu do CSV to bym w repozytorium nie umieścił, więc zostaje serwis 😀

Aleksander-32 napisał(a):

Na pewno eskport do CSV/pliku etc trzymałbym gdzies osobno jako jakiś inny byt

W rozumieniu data source, czymze wg Ciebie rozni sie system plikow od bazy danych lub zewnetrznego api?

Aleksander-32 napisał(a):

to uważam określenie typu "serwis" za bardzo niejednoznaczne i opisujące zbiór metod nie wiadomo co robiących.

gerundium lub jak kto woli odsłownik jako integralny element nazwy serwisu, plus czytanie ze zrozumieniem definicji S z Solid (chociaz wlasciwie istnieja dwie interpretacje co wprowadza duzo zamieszania), plus (co wielu traktuje jako hardcore) zasada single public method. Te elementy skutecznie pozwalaja uniknac Twoich watpliwosci.

markone_dev
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 godziny
  • Postów:810
6

Olej serwisy. Już wyżej koledzy zasugerowali bibliotekę MediatR i CQRS powinien być Twoją nową drogą. W dobie takich bibliotek jak MediatR tworzenie serwisów aplikacyjnych nad warstwą repozytorium nie ma sensu.

Z biblioteką MediatR masz handler per use case, czyli CreateUserHandler, FindUserHandler, AuthenticateUserHandler. Handler przyjmuje w konstruktorze jako argument repozytorium czy inny provider do storage'u, albo jak używasz Entity Framework to DbContext, handler coś tam robi, wywołuje query i zwraca wynik.

Osobiście traktuję to podejście z wieloma handlerami jako czytelniejsze niż posiadanie jednej klasy UserService z 20 metodami łącznie na ponad 1k linii kodu."

Do tego bym ci polecił nie używanie modelu domenowego do odczytu danych tylko robienie zapytań/projekcji w handlerze i mapowanie od razu na DTO.


Programujący korpo architekt chmurowy.
Udzielam konsultacji i szkoleń w obszarze szeroko pojętego cloud computingu (Azure, AWS) i architektury systemów IT. Dla firm i prywatnie.
DevOps to proces nie stanowisko.
edytowany 1x, ostatnio: markone_dev
somekind
Żeby tylko 1k...
RJ
Klasyczny problem tłustego repo i tłustego serwisu 😂
bbhzp
  • Rejestracja:około rok
  • Ostatnio:około 2 godziny
  • Postów:68
0
markone_dev napisał(a):

Osobiście traktuję to podejście z wieloma handlerami jako czytelniejsze niż posiadanie jednej klasy UserService z 20 metodami łącznie na ponad 1k linii kodu."

Dziękuję za odpowiedź, faktycznie czasem popełniałem takie klasy na 500 linii.... : )

Do tego bym ci polecił nie używanie modelu domenowego do odczytu danych tylko robienie zapytań/projekcji w handlerze i mapowanie od razu na DTO.

Masz rację, bo poleceń put, post i delete mam o wiele mniej i są one w miarę stałe, w przeciwieństwie do selectów, których mam tyle, ile jest różnych DTO : )

markone_dev
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 godziny
  • Postów:810
2
bbhzp napisał(a):

Dziękuję za odpowiedź, faktycznie czasem popełniałem takie klasy na 500 linii.... : )

To tyle co nic 😀

bbhzp napisał(a):

Masz rację, bo poleceń put, post i delete mam o wiele mniej i są one w miarę stałe, w przeciwieństwie do selectów, których mam tyle, ile jest różnych DTO : )

To była jedna z przyczyn dla których wymyślono CQRS a wcześniej CQS. W większości typowych aplikacji biznesowych zawsze będzie więcej odczytów niż zapisów i odczyty będą dużo bardziej złożone. A jak wiemy z teorii baz danych, model relacyjny sprawdza się doskonale przy zapisie, ale gdy przychodzi do odczytów, to im bardziej złożone zapytanie i im większy zbiór danych, tym bardziej trzeba się nagimnastykować i dobrze znać SQL-a, żeby go efektywnie napisać i wykonać. Jak na to nałoży się dodatkową abstrakcję w postaci ORM-a to już w ogóle papaj macha.jpg. Dlatego czasem w warstwie odczytowej rezygnuje się z ORM-a i pisze zapytania w SQL i używa Dappera, który nie daje takiego narzutu wydajnościowego jak choćby EF.


Programujący korpo architekt chmurowy.
Udzielam konsultacji i szkoleń w obszarze szeroko pojętego cloud computingu (Azure, AWS) i architektury systemów IT. Dla firm i prywatnie.
DevOps to proces nie stanowisko.
edytowany 1x, ostatnio: markone_dev
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)