Clean architecture, CQRS, RDMS, Spring IOC - pierwsze podejście

0

Cześć napisałem prostą aplikację komunikacyjną typu Messenger udostępniającą
REST api stosując Clean architecture i CQRS. Na razie żeby sobie uprościć
wykorzystuje RDMS jako bazę zarówno do zapisu jak i odczytu ale nic nie stoi
na przeszkodzie aby podpiąć np. Elasticsearch.

Byłoby fajnie dostać feedback od kogoś kto stosował któreś z wymienionych rozwiązań.

Repozytorium: messenger-backend

2

NUDA.. To jest kod, który nic nie robi. Skakałem po projekcie oglądając niemal puste klasy.., ale poza tym to dwie rzeczy zwróciły moją uwagę:

Klasa AbstractEntity jest trochę dziwna narzuca jeden rodzaj ID, który w dodatku można go zmieniać. Przez co porównanie może z czasem zaskoczyć.

Hash liczysz na danych, które mogą się zmienić np. NotificationCreateCommand taki hash, który się zmienia w czasie więc też może zaskoczyć :-)

Część klas tylko przetrzymuje dane, i służą tylko do podawania dalej. W przypadku takich klas kod byłby prostszy jakby te klasy operowały niemodyfikowalnymi wartościami, ale nie wiem czy to byłoby proste do spięcia z JPA.

0

Dzięki za przejrzenie kodu, to moje pierwsze podejście z clean architecture
i cqrs więc na razie chciałem sprawdzić jak będzie to funkcjonowało w ramach
całego projektu.

Hash liczysz na danych, które mogą się zmienić np. NotificationCreateCommand taki hash, który się zmienia w czasie więc też może zaskoczyć :-)

Możesz doprecyzować, co w tej klasie się zmienia w czasie?

Część klas tylko przetrzymuje dane, i służą tylko do podawania dalej. W przypadku takich klas kod byłby prostszy jakby te klasy operowały niemodyfikowalnymi wartościami, ale nie wiem czy to byłoby proste do spięcia z JPA.

Jakie dokładnie klasy masz na myśli? Klasy domeny są niezmienne, chodzi Ci o DTO?

0
eziomou napisał(a):
Hash liczysz na danych, które mogą się zmienić np. NotificationCreateCommand taki hash, który się zmienia w czasie więc też może zaskoczyć :-)

Możesz doprecyzować?

Hash nie może się zmieniać podczas życia obiektu. Jeśli się zmieni wiele kolekcji może przestać działać, np HashMap i HashSet

0
KamilAdam napisał(a):
eziomou napisał(a):
Hash liczysz na danych, które mogą się zmienić np. NotificationCreateCommand taki hash, który się zmienia w czasie więc też może zaskoczyć :-)

Możesz doprecyzować?

Hash nie może się zmieniać podczas życia obiektu. Jeśli się zmieni wiele kolekcji może przestać działać, np HashMap i HashSet

Jasne, chodzi o to że przekazuje set i nie robię kopii? Jeśli tak to oczywiście błąd z mojej strony dzięki za uwagę.

5

Sprawdziłam tylko kilka klas - wszystkiego mi się nie chciało. Odnośnie ID obejrzyj to a będziesz wiedzieć co robisz tam źle:

  1. stosujesz przestarzałego mavena
  2. mnóstwo boilerplate'u - niepotrzebnie wydzielasz tyle osobnych modułów - zastosuj monolit i pakiety javowe. tutaj podział na takie moduły jak zrobiłeś nie dał Ci żadnej przewagi
  3. nie dziel za pomocą warstw typu persistance, core itd.. a funkcji biznesowych
  4. AbstractEntity - może nazwij badziej UuidEntity - przede wszystkim Id zmień na UUID i nadawaj to od razu, setterów do id nie rób - id niech się nie zmienia albo masz je z bazy albo od razu nadajesz UUID, stosuj więcej final
  5. nie stosuj sekwencji z bazy ani hibernetowych - to są zjeb*** mechanizmy
  6. poco postfix "Model" na encjach?
  7. gettery i settery -> lombok albo najlepiej zmień język na Kotlin
  8. @Column(nullable = false) - czy jak to zastosujesz to jak tworzy Ci się schemat z encji to robi constraint not null na tym polu?
  9. mapper spoko, ale nie rozumiem czemu nadużywasz @Component - stosuj hexagonala
  10. @Repository spoko, ale możesz nawet wydzielić fajniejszy interfejs dla repozytoriów swoich aby za każdym razem dla encji, które dziedziczą po AbstractEntity i mają zawsze ID typu Long aby nie musieć powtarzać tego extends JpaRepository<UserDataModel, Long>, będziesz mieć łatwiej, zerknij na dział "Repozytorium dla encji" https://4programmers.net/Forum/Java/281698-uuid_version_i_spring_data_na_co_trzeba_uwazac
  11. Optionale w repo - najs
  12. boolean existsByUsername(String username); - jeżeli będziesz przed jakimś zapisem korzystać z tej metody i gdzieś sprawdzać najpierw czy exists aby nie poleciał Ci wyjątek to jest to błąd check-then-act
  13. UserJpaReadonlyRepository - a adnotacja to @Component? dodaj lomboka, klasa trochę widmuszka
  14. UserJpaRepository - najs, że robisz takie nazwy klas (lepsze to niż Impl)
  15. UserMapper - prosta fuinkcja od razu @Componenet - nadużywasz springa, pisz czysto w javie a w springu rejestruj fasady całych modułów większych
  16. NotificationTypeDataModelProvider orElseGet i te type.setName(s); słabo to wygląda

ogółem 7/10 więc w miarę spoko

0

nie dziel za pomocą warstw typu persistance, core itd.. a funkcji biznesowych

Argument bez sensu kiedy masz taki mikro projekt który realizuje jakąś jedną małą funkcje biznesową. Zresztą jest to dodatkowo bez sensu, bo o ile nie zrobisz tego z głową (tzn nie wydzielisz interfejsów dla interakcji np. z repozytoriami i do komunikacji z innymi serwisami) to spora szansa ze zrobisz potworka gdzie logika domenowa jest pomieszana ze szczegółami implementacji takich rzeczy jak persystencja czy komunikacja z innymi serwisami.
Więc spoko, jak masz monolit to ma to sens. Tutaj, nie ma.

0

@karolinaa
Wielke dzięki za przejrzenie kodu i uwagi.

    1. Rzeczywiście w przypadku takiego malutkiego projektu jest to przerost formy nad treścią jednak tak jak w tytule tematu, chciałem wypróbować jak najwięcej z Clean Architecture i CQRS.
    1. Jak wyżej w przypadku małego projektu tego nie widać, ale dzięki podziale na moduły w przypadku większego projektu osobne zespoły mogą je osobnie kompilować, wersjonować itd.
    1. 5, 10 Dzięki za uwagę, już zastanawiałem się nad UUID tylko nie wiem czy jako klucz główny i we wszystkich tabelach, jednak 16 bajtów a 4 lub 8 robi różnicę.
    1. W celu konsekwentego nazewnictwa - mam XQueryModel dla modeli widoku więc modele bazonadowe to XDataModel, poza tym wydaje mi się, że taka nazwa mówi więcej niż np. XData.
    1. 13 Szczerze mówiąc nie przepadam za Lombokiem, może Kotlin.
    1. Tak
    1. 15 OCP, nastawiam się, że powstawie więcej klas domeny więc i więcej klas mapujących, musiałbym wtedy za każdym razem dodawać je do fasady. Skoro moduł persistence-jpa jest już i tak zależny od Springa więc czemu sobie nie ułatwić?
    1. Repozytoria XReadonlyRepository wykorzystywane są tylko przez warstwę aplikacji, ale w klasie UserDataModel rzeczywiscie brakowało wymuszenia unikalności (@Column(unique = true))
1
semicolon napisał(a):

NUDA.. To jest kod, który nic nie robi. Skakałem po projekcie oglądając niemal puste klasy.., ale poza tym to dwie rzeczy zwróciły moją uwagę:

Im więcej buzzwordów i technologii tym mniej program robi
George Stanley McGovern

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