DTO - przykład

0

Nie bardzo rozumiem o co w tym chodzi, a ciężko znaleźć jakiś prosty przykład, w którym będzie opisanie oprócz tego 'co' to jeszcze 'dlaczego'. Staram się naśladować znaleziony w sieci kod, ale czasami jest to dla mnie nielogiczne. Poniżej przykładowy kontroler:

    @RequestMapping(value = "/contact/new", method = RequestMethod.POST)
    public String addContact(@Valid Contact contact, BindingResult result,
                             SessionStatus status, RedirectAttributes attributes) {
        if (result.hasErrors()) {
            return CONTACT_FORM_VIEW;
        } else {
            ContactDTO contactDTO = ContactUtils.contactToContactDTO(contact);
            Contact added = contactService.add(contactDTO);
            logger.info("Added contact with information: {}", added);
            status.setComplete();

            addFeedbackMessage(attributes, FEEDBACK_MESSAGE_KEY_CONTACT_ADDED,
                    added.getFirstName(), added.getLastName());

            return "redirect:/contacts";
        }
    }

Jeżeli cokolwiek zrozumiałem z DTO, to chodzi o to, żeby w ramach logiki biznesowej operować na obiekcie, ale do klienta przekazywać DTO i od niego odbierać DTO - czyli uproszczony obiekt, niezawierający żadnych metod (to tak w skrócie). W takim razie dlaczego w powyższym kodzie odbieramy obiekt, następnie zamieniamy go na DTO, po to tylko, żeby za chwilę zamienić go z DTO z powrotem na obiekt, żeby dodać go do bazy (metoda add zamienia DTO na obiekt).

Z kolei poniżej przekazujemy do modelu obiekt, a nie DTO.

    @RequestMapping(value = "/contact/{contactId}", method = GET)
    public String contactDisplayPage(@PathVariable("contactId") Long id, Model model)
            throws ContactNotFoundException {
        logger.info("Showing contact page for contact with id: {}", id);

        Contact found = contactService.findContactById(id);
        logger.info("Found contact: {}", found);

        model.addAttribute(MODEL_ATTRIBUTE_CONTACT, found);
        return CONTACT_VIEW;
    }

Nie wiem czy to wszystko jest OK (a jeżeli tak to czemu akurat tak się to robi), czy może kod nie jest jeszcze 'posprzątany'. Czy przy małych projekcikach jest sens stosowania w ogóle DTO? Czy nie powoduje to tylko zmniejszenia czytelności kodu i większego nakładu pracy?

Całość kodu tu: https://github.com/mintster/spring-data/tree/v0.1.9

1

Co do sensu stosowania DTO to mam dla Ciebie 3 odpowiedzi:

  1. Większość programistów nie ma pojecia po co jest DTO i stosuje, bo wyczytali że tak trzeba, albo koledzy powiedzieli.
    To się nazywa Cargo cult programming. Czyli bez sensu. Twój przykład kodu idealnie ilustruje taki bezsens.

  2. W teorii DTO ma największy sens kiedy struktura obiektów modelu odbiega od tego co prezentujesz na ekranie.
    Czyli w bazie masz 3 postac normalna itp., a "na ekran" chcesz przekazać obiekty z już ze zagregowanymi danymi, pojoinowane, pofiltrowane itp.

  3. W praktyce niestety DTO stosować prawie zawsze musisz o ile korzystasz z JPA (Hibernate czy podobnego frameworku). Albowiem są one (te frameworki) popsute. Chodzi o to, że JPA działa na zasadzie tzw. managed obiekt i dirty check - czyli obiektów które są "live" podpięte do bazy danych.
    Jak gdzieś coś w takim obiekcie zmienisz to samo się zapisze do bazy danych. Czasem nawet nie zauważysz kiedy.
    Niestosowanie DTO może skutkować trudnymi do ogarnięcia błędami (mój ulubiony to np. podłączenie w relacji jeden do wielu obiektów podciągniętych w różnych transakcjach ... miodzio).
    DTO jest takim patternem, żeby się szybko "wypiąć" z JPA i ominąć tego typu błędy.
    Ale IMO lepszym rozwiązaniem jest od razu używać wtedy "select new MyDTO" w repozytoriach. albo... używać nie aż tak bardzo popsutego frameworku (czyli np. JOOQ).

Może inaczej: JPA czy Hibernate nie są aż tak bardzo popsute. Są nawet genialne... tylko nie nadają się do robienia typowych backendow opartych o SQL.

0

Dobra @jarekr000000 i pytanie co zrobisz jak zmienisz encje a nie chcesz zmienić tego co leci na front? Poza tym całkiem dobre jest to że front jest oddzielony od bazy danych za tą "fasadą" DT0 :)

1

@scibi92 właśnie wtedy mogę wprowadzić DTO... kiedy bedę miał ten problem, ale nie wcześniej. (YAGNI).
Co do tego całkiem dobre bla bla bla. IMO nie wiesz dlaczego całkiem dobre :-)
W typowym REST serwisie mamy już izolację na poziomie JSON/REST i ten DTO (jeśli jest trywialny) to jednak nonsensowne przelewanie z geterów w setery.
W przypadku (już rzadko spotykanym), robienia czegoś w JavaFX czy Swing taka izolacja (model / view model ) ma nadal sens nawet dla trywialnych przypadków.

0

A co do JPA nie masz racji. Encje zawsze możesz "rozłączyć" -> http://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html#detach-java.lang.Object-

0

No pewnie. Wystarczy tylko przejrzeć cały kod i zobaczyć czy encje są rozłączone czy nie. Na tym polega sens programowania obiektowego...

0

Nie mowię czy to dobre czy złe, stwierdzam fakt.
EDIT:
poza tym nie widziałem jeszcze przypadku żeby odwzorowanie encji na obiekt JSONowy 1:1 było dobre, poza jakimiś bieda tutorialami :)

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