Projektowanie obiektowe - zapisanie modelu do bazy danych

0

Cześć, tworzę aplikację webową w Spring MVC i zastanawiam się, które rozwiązanie będzie bardziej prawidłowe. Chcę zapisać klasę Profile do bazy i wygląda to tak, że użytkownik wypełnia formularz na stronie i jest przesyłany do ProfileController. Mam jeszcze klasę ProfileService. I teraz nie wiem czy w kontrolerze zrobić tak, że:

  • dodać zależność Profile i przesłać formularz do Profile profile.createProfile(profileForm), a później przesłać formularz do serwisu i do bazy
  • czy dodać zależność ProfileService i przesłać formularz do serwisu i dalej do bazy

Kod do drugiej opcji

public class ProfileService {

    private ProfileDao profileDao;

    @Autowired
    public ProfileService(ProfileDao profileDao) {
        this.profileDao = profileDao;
    }

    public void createProfile(ProfileForm profileForm) {
        Profile profile = profileForm.getAsProfile();
        profileDao.save(profile);
    }
}

Lub może jeszcze coś innego?

Czy ktoś mógłby mi pomóc? Nie chciałbym tu zrobić overengineering'u i zachować prawdiłowe zasady obiektowości. Pozdrawiam.

3

To taką zasadą powinno być nie stosowanie armaty na muchę -> tzw. "Service" nie ma tu żadnej racji bytu. Po prostu z controllera zawałaj repository, bo nie masz żadnej logiki do przetwarzania obiektu.

2

Ta druga wersja (z serwisem, w którym odbywa się mapowanie z danych forma na persistence model) wygląda sensownie. Jest zgodnie z wzorcem MVC, a jak Ci dojdzie logika, to będziesz mógł tam ją łatwo dodać.

Wpychanie jakiegoś repository do controllera, to gwałt na repozytoriach i MVC.

0

@somekind @WeiXiao no cóż, jestem zaskoczony, prawde powiedziawszy nie spodziewałem się że ktoś stwierdzi że jestem zwolennikiem spaghetti code, ile można się o sobie dowiedziec.
Może to dziwne że widzę różnicę między 70 linijkami w metodzie controllera w wywołaniem jednej metody repository.save(cośtam). Może to tez zaskawiwac, ale uwaga jak powstaje logika biznesowa zawsze można dodac tą klase "service". No widze że niektórzy lubią sobie tworzyć klasy żeby były :D

0

Dobrze, po prostu nie wiesz co to repozytoria i MVC. Ale nie przejmuj się, większość ludzi tego nie rozumie.

1

A możesz w takim razie wyjaśnić czemu to się nie zgadza z MVC? Bo jestem ciekaw :)

4

Bo kontroler służy do obsługi akcji użytkownika, a nie orkiestracji logiki biznesowej: http://commitandrun.pl/2016/05/30/Brutalne_prawdy_o_MVC/

0

@somekind: no masz racje, tylko... tu nie ma logiki biznesowej? Tu jest po prostu CRUD. Trudno mi to nazwac logiką bizensową...

2

Ech, no teraz będzie czepianie się od definicje. Może logiki w tym dużo nie ma, ale przechowanie jakichś danych to nadal wymaganie biznesowe.
I nie ma powodu, żeby kontroler (którego zadaniem jest obsługa inputu) dostawał zależności od ORM/DAO i organizował operacje crudowe. SRP I takie tam.

0

@Shalom: a Ty co sądzisz?

4

Zgodnie ze sztuką jest tak jak @somekind napisał, nie chcemy ani encji na twarz, ani orkiestracji logiki biznesowej w kontrolerach. Natomiast z pragmatycznego punktu widzenia jeśli piszemy jakiegoś małego i mało znaczącego cruda bez testów automatycznych, to jak zaczniemy z logiką w kontrolerach i będziemy pchać encję to wiele złego się nie stanie. Ważne by wyczuć odpowiedni moment kiedy jednak to podejście przestaje nam się opłacać i zrefaktoryzować je do wersji zgodnej ze sztuką, zanim zacznie zagrażać nam wizja przemienienia się projektu w wielką kulę błota.

0

Zgodnie ze sztuką jest tak jak @somekind napisał, nie chcemy ani encji na twarz, ani orkiestracji logiki biznesowej w kontrolerach. Natomiast z pragmatycznego punktu widzenia jeśli piszemy jakiegoś małego i mało znaczącego cruda bez testów automatycznych, to jak zaczniemy z logiką w kontrolerach i będziemy pchać encję to wiele złego się nie stanie. Ważne by wyczuć odpowiedni moment kiedy jednak to podejście przestaje nam się opłacać i zrefaktoryzować je do wersji zgodnej ze sztuką, zanim zacznie zagrażać nam wizja przemienienia się projektu w wielką kulę błota.

Moim zdaniem najlepszy moment do tego jest teraz :| Ja np. do WebApi mam jeden komponent (dll), który razem z kontrolerami generuje mi całego cruda ja tylko pokazuje mu namepsace z persistences ustawiam request model i result model ewentualnie ustawiam dodatkowy hateoas przez "hooki". I to wszystko.

2

@neves - ale to nie jest pragmatyczne, to zaciągnięcie długu technicznego, który absolutnie niczego nie daje. W obu podejściach kodu jest tyle samo i tak samo trudno/łatwo się go pisze, różnica jest tylko w organizacji klas. A skoro wraz z rozwojem aplikacji może zajść potrzeba refaktoryzacji, to pragmatyczne jest uniknięcie tego i zastosowanie prawidłowej struktury klas od początku.

0

Dzięki za odpowiedzi
@scibi92: na razie nie ma logiki, bo dopiero zaczynam tworzyć
@somekind: zakładając, że jednak miałbym pewną logikę dotyczącą klasy Profile, którą chciałbym zaprogramować to powinna być ona w serwisie? A model to tylko entity z getterami i setterami? Czytałem gdzieś, że w modelu powinna być jakaś logika dlatego pytam. Nie chciałbym też tworzyć logiki dla samej logiki, żeby po prostu była w modelu.

2
somekind napisał(a):

Bo kontroler służy do obsługi akcji użytkownika, a nie orkiestracji logiki biznesowej: http://commitandrun.pl/2016/05/30/Brutalne_prawdy_o_MVC/

Przeczytałem wątek bardzo starannie. Ja w tym wątku zajmuję ogólną pozycję "nie za dużo warstw". Zwłaszcza z w/w bloga sąsiedni artykuł mnie zachęcił do odezwania się.
http://commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/

*Z 1. i 4. nie ma co dyskutować, natomiast 2. i 3. są w dzisiejszych czasach (14 lat po pierwszej publikacji tej książki) dość dyskusyjne, albowiem:
*
(odnoszę się ogólnie, nie wnikam co to jest 1...4)
Lata zmieniły nie tylko stronę "fizyczną" (jak artykuł mówi dosłownie "dziś są lepsze niż dawniej ORM"), ale wiele w "logicznej".

Z jednej strony jednolijjkowe *) deklarowane repozytoria / DAO. Z drugiej mniej monolitów na rzecz dzielenia (by nie użyć "religijnego" słowa mikrousługa). Z trzeciej mniejszy dystans czasowy życia oprogramowania (lub głębsze sobie uświadomienie krótkości życia). Większość scenariuszy "użyjmy więcej warstw bo kiedyś się przyda" nie zachodzi. Siła nowszych bibliotek / frameworków (sorry, jednilijkowa adnotacja/atrybut to jest MNIEJ KODU niż 50 linii "dawnych')

Nawet sam teoretyczny MVC dziś już jest podważany przez "heretyków", co kiedyś było niedopuszczalne. Wiele realnego oprogramowania jest "podobne do MVC" i nikt nie ma poczucia winy, czy to jeszcze trochę podobny MVVC, czy już całkiem inne architektury. Na przykład - tak, świadomie używam "modnego" słowa pozornie z innej dziedziny - REST. Dodam CQRS.

*) w sensie jedna linia wyłącznie deklaracji metody, generowane automatycznie. Leciutka warstwa - prawie "nie-warstwa", a niech konserwatyści mają warstwę. Ale jest "encja na twarz", nikt od tego nie umiera.

2
somekind napisał(a):

@neves - ale to nie jest pragmatyczne, to zaciągnięcie długu technicznego, który absolutnie niczego nie daje. W obu podejściach kodu jest tyle samo i tak samo trudno/łatwo się go pisze, różnica jest tylko w organizacji klas. A skoro wraz z rozwojem aplikacji może zajść potrzeba refaktoryzacji, to pragmatyczne jest uniknięcie tego i zastosowanie prawidłowej struktury klas od początku.

I taka refaktoryzacja w postaci dodania jednej kalasy po środku pewnie by położyła projekt...
Jestem totalnie przeciw tworzeniu klas na zapas - żeby było ładnie i na przyszłość - bo jest to nieładne i nie przydaje się w przyszłości :-)

Praktyka pokazuje, że i tak zwykle design jest zbyt ciasny tam gdzie nie przewidzieliśmy i zbyt rozdmuchany tam gdzie wcale nie trzeba.
YAGNI działa, narzędzia do refaktoringu działają. Lata 90-te się skończyły.

0

I taka refaktoryzacja w postaci dodania jednej kalasy po środku pewnie by położyła projekt...
Jestem totalnie przeciw tworzeniu klas na zapas - żeby było ładnie i na przyszłość - bo jest to nieładne i nie przydaje się w przyszłości :-)

Praktyka pokazuje, że i tak zwykle design jest zbyt ciasny tam gdzie nie przewidzieliśmy i zbyt rozdmuchany tam gdzie wcale nie trzeba.
YAGNI działa, narzędzia do refaktoringu działają. Lata 90-te się skończyły.

A jak się nazywa ten Design, w którym robi się sobie problemy, żeby je potem refaktoryzować. Masz racje najlepiej poczekać aż taka klasa do refaktoryzacji nasiąknie zależnościami wtedy jest lepsza frajda z używania narzędzi do refaktoryzacji. :|

Najlepsze to są unit testy pisane zgodnie z YAGNI ;|

3
._. napisał(a):

A jak się nazywa ten Design, w którym robi się sobie problemy, żeby je potem refaktoryzować. Masz racje najlepiej poczekać aż taka klasa do refaktoryzacji nasiąknie zależnościami wtedy jest lepsza frajda z używania narzędzi do refaktoryzacji. :|

Najlepsze to są unit testy pisane zgodnie z YAGNI ;|

Jeśli nie masz narzędzi do kontroli jakości kodu, najprostszego nawet review, ew. metryk itp. - to żadne wzorce, warstwy itp. Cie nie uratują.

0

A ja uważam że najlepszym wzorcem projektowym jest zdrowy rozsądek. Widziałem serwisy z modelem interface + 1 implementacja, a w nich ifologia gdzie trzeba byłoby normalnie skorzystać np. z architektury plug-in i polimorfizmu (Open-Close Principle) :D

0

Jeśli nie masz narzędzi do kontroli jakości kodu, najprostszego nawet review, ew. metryk itp. - to żadne wzorce, warstwy itp. Cie nie uratują.

Nie, żebym się czepiał, ale przed czym mnie uratują metryki ziarnistości paczek, czy metod, oraz % pokrycia testami. Szczerze to wolę się kierować rozsądkiem a nie obliczeniami ziarnistości czy procentowi testów.

0

Nie chce być zlośliwy ale dziwne wygląda pisanie o roządku i jednoczesnym dodawaniem jakiejś warstwy "logiki" gdzie nie ma żadnej logiki tylko wywowałenie jakiejś 1 metody z DAO :D

0

A DAO to nie jest logika tylko że infrastructury ?

0

No DAO to logika operacji dostępu do danych, nie jest to logika "biznesowa". Tutaj jej nie ma...

0

A dlaczego biznesowa a nie aplikacyjna, a aplikacyjnej tam nie ma? ;)

Warstwę aplikacyjną robi się, aby zakreślić "kotnrakt" zachowanie aplikacji. Zwykle perstenceModel jest w warstwie infra a model, który zwracasz dalej Result, czy DTO trzymasz w warstwie aplikacyjnej.

2
discoStar napisał(a):

Czytałem gdzieś, że w modelu powinna być jakaś logika dlatego pytam. Nie chciałbym też tworzyć logiki dla samej logiki, żeby po prostu była w modelu.

To zależy o jaki model Ci chodzi. W modelu domenowym powinna być logika, bo biznesowy rdzeń aplikacji, a w modelu perzystencji nie, bo to tylko struktura rekordów w miejscu przechowywania danych.

0

@somekind: Chodziło mi o to czy ma być w modelu domenowym. Czyli mam robić osobno model domenowy i persystencji (entity) czy w jednej klasie?

0

Wpychanie jakiegoś repository do controllera, to gwałt na repozytoriach i MVC.

@somekind Jeśli chcemy tylko wyświetlić jakieś dane z bazy to jak to zrobić zgodnie z MVC? Ja w takim przypadku robię jak poniżej.

public class StudentController : Controller
    {
        var studentRepo;

        public StudentController()
        {
            studentRepo = new StudentRepository();
        }

        public IActionResult Index()
        {
            StudentsViewModel viewModel = new StudentsViewModel(studentRepo.GetForIndex());
            //albo w przypadku EF
            //StudentsViewModel viewModel = new StudentsViewModel(context.Students.ToList()); 

            return View(viewModel);
        }
    }

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