Architektura kodu

0

Witam,
chciałem się zapytać jak tworzycie bardziej zaawansowane algorytmy jeśli chodzi o struktury w kodzie nawet jeśli weźmiemy na tapetę "hexagonal architecture" oraz DDD gdzie centrum wszystkiego jest logika biznesowa to rodzi to u nas pewne problemy

Wyobraźmy sobie flow

Controller ->Application Service(UseCase, DTO) -> Domain Logic(DDD) -> Repo (Aka Hibernate)

Już na pierwszy rzut oka mamy do czynienia z trzeba strukturami obiektów:

  1. DTO - które spływa od klienta
  2. BusinessModel - jako obiekty dla obliczeń logiki biznesowej
  3. Entity jako dane do zapisu w bazie danych

konwersja tych typów jest bardzo męcząca dodatkowo rodzi problemy takie jak np. walidacja odbywa się na BusinessModel i teraz znów w jakiś magiczny sposób musimy przekształcać wyjątki z BusinessModel na te zrozumiał dla usera, np pole ilość przekracza douszczalny stan i teraz musimy zapalić lampeczke na GUI ale nie mamy powiązania GUI/DTO ->BusinessModel

2
  1. Nie rozumiem trochę o jakiej konwersji mówisz. Warstwa logiki biznesowej to są zwykle Serwisy i mogą operować na tych DTO bez problemów. Ba, nawet powinny bo i czemu by nie.
  2. Nie rozumiem twojego problemu z wyjątkami bo powiązanie o którym mówisz istnieje i nazywa się Kontroler. Kontroler pobiera request od usera, woła metodę logiki biznesowej a potem pcha dane do odpowiedniego widoku. Jak poleci wyjątek to właśnie na kontrolerze się zatrzyma a ten pchnie do usera odpowiednie "dane" i odpowiedni "widok", w tym przypadku info o błędzie.
0

Generalnie spotkałem się z różnymi podejściami w tego typu klasycznym stylu jeśli chodzi o wspomnianą "konwersję" i zarządzanie wyjątkami, natomiast ja osobiście preferuję kilka prostych rozwiązań usprawniających całą zabawę:

  1. Konwersja pomiędzy DTO i encjami: tutaj także spotkałem się z różnymi podejściami. Niektórzy preferują ręczną konwersję kreując klasy assemblery w tym celu, unikając jakiejkolwiek refleksji. Ma to swoje plusy w postaci pełnej kontroli konwersji. Minusem jest fakt, iż pola dto i encji mogą się znacznie rozjechać. Ja osobiście preferuję skorzystanie z metody copyProperties z BeanUtils ze Springa czy Apache Commons w celu szybkiej konwersji encji do dto. Plusem tego podejścia jest prostota i oszczędność czasu (nie trzeba pisać assemblerów). Jeszcze lepiej do kopiowania działa Dozer. Konieczność trzymania nazw pól w DTO i encjach spójnych też imho jest plusem. Niestety, przy tym podejściu każda zmiana w modelu / dto przemknie cichaczem i może objawić się NPE. Często i tak nie obejdzie się bez assemblerów, gdy trzeba będzie coś zaimplementować, co nie jest w encjach. W takich przypadkach lepiej jednak już pisać assemblery.
    W przypadku konwersji z DTO do encji (lub czegokolwiek innego w warstwie DAO) można zawsze posłużyc się walidacyjnymi propertisami z jsr303. Ale o tym w drugim punkcie.
  2. Jeśli chodzi o kwestię wyjątków i interfejsu użytkownika, tutaj wg mnie warto stworzyć sobie własne klasy wyjątków, które mogą posłużyć jako wrappery do wyjątków wyrzucanych czy to z warstwy serwisu, czy z DAO. Dodatkowo, mogą rozszerzać RuntimeException, dlatego możemy zapomnieć o zbytecznych try-catchach. Każdej instancji takiego wyjątku możesz nadać własne UUID (bardzo użyteczne w szukaniu problemu w logach), skorzystać z i18n w celu konwersji językowej wiadomości dla użytkownika czy pogrupować je jakimś polem enumem w grupy specyficznych wyjątków aplikacyjnych, co potem pozwoli na skuteczniejszy debug grepując logi. Warto skorzystac także z AOP nie tylko do logowania, ale choćby łapania tego typu bąbelkujących wyjątków. Np Springowego RestControllerAdvice, w którym możesz sobie łapać poszczególne klasy wyjątków wyrzucanych z kontrolerów. Daje Ci to duże możliwości: łapiesz wyjątek rzucany z czeluści backendu, logujesz stack trace, tłumaczysz wiadomość dla użytkownika poprzez serwis i18n, ustawiasz http status i tworzysz pięknego jsona z błędem zwracanego przez kontroler. Później możesz sobie takiego jsona dalej przetworzyć (np tworząc http interceptora po stronie frontu, np httpInterceptor w Angularze). Możesz użytkownikowi wyświetlić UUID błędu, co ułatwia wszelakie grepy po logach.
    Skorzystaj także z walidacji JSR303, oczywiście wcześniej warto się zabezpieczyć po stronie frontu, by nie robić niepotrzebnych calli do backendu. Jeśli nawet użytkownik wprowadzi błędne dane i przejdzie to walidację od strony UI, to jsr303 automatycznie wygeneruje wyjątek (choćby MethodArgumentNotValidException), który znowu przechwyci RestControllerAdvice i wypchnie elegancko do wiadomości użytkownikowi. Jeśli dojdzie do warstwy biznesowej i tam znowu zostanie rzucony wyjątek, to i tak wybąbelkuje wzwyż i zostanie złapane przez advice, wskutek czego użytkownik dostanie jakiś ładny generyczny komunikat.

Uff, trochę się rozpisałem, ale tego typu automatyzacja trochę uprzyjemnia zabawę z wyjątkami i konwersją.

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