Kiedy konwertować na DTO?

0

Hej, mam mikroserwis, który składa się z warstwy kontrolerów, serwisowej, repozytorium (tyle mi wystarcza do spełnienia założeń biznesowych, wiem że jest DDD itd.)

W warstwie kontrolerów mam metodę POST, która przyjmuje JSON'a, następnie w warstwie serwisowej ten Json jest parsowany na obiekt, dokonywane są na nim pewne obliczenia i ten obiekt trafia przez warstwę repozytorium prosto do bazy danych. Czy potrzebuję tutaj używać Mappera?

Flow mojego mikroserwisu przebiega w następujący sposób.

call z rest api do mojego serwisu ---> odebranie jsona w kontrolerze i przekazanie go do warstwy serwisowej ---> parsowanie jsona na obiekt który ma być zapisany w bazie danych w warstwie serwisowej ---> zapisanie obiektu do bazy poprzez warstwę repozytorium.

W jakich przypadkach powinienem używać mapperów do mapowania obiektów wyciąganych z bazy danych i mapowania ich na DTO? Jeżeli mówię o MVC to w której warstwie się to dzieje? Dziękuję za odpowiedź

2

Generalnie logika powinna się opierać o model domenowy który nie jest modelem bazodanowym i nie powinen mieć żadnych zależności do JPA, Spring Data etc.
Prawdziwie repozytorium w API ma obiekty domenowe np.
Option<Product> findProductById(ProductId productId), gdzie Product to obiekt domenowy. Implementacja repozytorium może wywoływać jakieś niskopoziomowe DAO.
Dodatkowe DTOsy na poziomie API, baz danych, kolejek, websocketów powinno powinny być dodane jeśli sa nietoższame z obiektami domenowymi w 100%, czyli np. mają jakieś adnotacje Jacksona, mają inne pola, etc - czyli w rzeczywistym świecie programowania prawie zawsze :D

W warstwie kontrolerów mam metodę POST, która przyjmuje JSON'a, następnie w warstwie serwisowej ten Json jest parsowany na obiekt

Że co? A jak przekazujesz ten JSON z kontrolerów? Jako String? oO

3

Jak zadajesz takie pytanie, to prawdopodobnie powinienes zrobic to jakos tak tak:

@RestController
@RequestMapping(path = "/lessons")
public interface LessonRepository extends JpaRepository<Lesson, Long>
{
    @GetMapping("/field/{name}")
    Page<Lesson> findAllByCourseSpecializationFieldOfStudyName(@PathVariable("name") String name, Pageable pageable);

    @GetMapping
    Page<Lesson> findAll(Pageable pageable);

    @GetMapping("/course/{courseName}")
    Page<Lesson> findAllByCourseName(Pageable pageable, @PathVariable String courseName);

}

Czyli kontroller jest jednoczesnie DAO i serwisem w jednym. Nie ma sensu ani jakis bardziej wyszukany podział ani tym bardziej architektura lazania. Tak w miare szybko mozna naklepac CRUD'a w springu

EDIT: A jak masz obliczenia to wydziel do osobnej klasy dostepem do DAO, ewentualnie jak to jakas drobnica to mozesz rownie dobrze w tym interfejsie zaimplementowac jako default metod, tak naprawde zalezy ile tego (logiki) jest.

1

Też mam wrażenie ze ty potrzebujesz Spring-Data-Rest jakieś, skoro po prostu robisz RESTową nakładkę na tabele w bazie danych ;)

1

(tyle mi wystarcza do spełnienia założeń biznesowych, wiem że jest DDD itd.)

Nie trzeba się z tego tłumaczyć. Tysiące aplikacji to CRUDy. Użycie DDD jak piszę się CRUDa to "overengineering", po polsku przekombinowane.

Gdybym to ja był autorem to najprawdopodobniej dokonał bym konwersji JSON <-> DTO już na etapie kontrolera. Dlaczego?

  1. Fail fast - nieprawidłowy JSON nie dotrze nawet do kontrolera. Zamiast tego twój framework zwróci dla użytkownika 4xx.
  2. Jeżeli chciałbyś dodać walidację to możesz użyć JSR 303 (czyli dodać annotację na gettery DTOsa). Z JSONem będzie trudniej.
  3. Security, praktyka znana pod nazwą OVERPOSTING. Jeżeli masz mapowanie map(json, entity) to może się okazać że atakujący doda do JSON pole które nie jest udokumentowane w API ale w encji występuje i zmieni jego wartość.
  4. Elastyczność, zapewne po pewnym czasie nastąpi rozjazd między strukturą danych w DB a tym co jest w JSON'ie. Np. userName w bazie jako ID a w JSON'ie jako email. Mapowanie JSON <-> Entity stanie się coraz bardziej skomplikowane. Będzie łatwiej gdy taka logika mapująca będzie w całości w kodzie.
  5. Co się stanie jak ktoś powie że trzeba też dodać wsparcie dla XMLa? Do DTO można mapować i z JSONa i z XML i z gRPC, ba pewnie GrapahQL też da się zmusić do współpracy.
  6. Twoje API powinno być udokumentowane (np. OpenAPI aka Swagger) Łatwo to zrobić dodając adnotacje na gettery (choć można też użyć plików JSON lub YAML - ale nie polecam bo tooling jest jeszcze niedojrzały).

Generalnie architektura 3-warstwowa opiera się na tym że serwisy zwracają/przyjmują DTO.

1

IMO praktycznie nigdy nie potrzebujemy wystawiac calej bazy danych a poszczegolne use casy.
Teoretycznie wszedzie powinienes miec jakis obiekt per pakiet i sobie mapowac do niego i na nim operowac i nie mieć brzydkich importow z innych pakietow.
Przy prostych appkach mozna po prostu zrobic sobie funkcje to i from w dto i encjach i uzywac kiedy potrzeba.
I troche na zasadzie Port & Adapters, na wejsciu, wyjsciu z danego pakietu domenowego transformujemy na to co potrzebujemy.
Z tym, że to też moze byc przerost formy nad tresciom jak nam sie kod zamienia w kolko mapowanie z obiektu na obiekt, bo teoretycznie potrzebujemy aż trzech... reprezentacji JSON, Encji bazodanowej i biznesowy obiekt.
Pakiet domenowy = logika biznesowa, brak warstwy infry, czy np. jakis tam controllerow itp.

DDD owszem, ale jego przeznaczenie jest dla skomplikowanych domen biznesowych.

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