ActiveRecord a logika biznesowa

Jeżeli się nie mylę to w Railsach logika biznesowa powinna znajdować się w modelach… Czy tak?

Natomiast właśnie czytam książkę “Clean Code” Roberta C. Martina (http://helion.pl/ksiazki/czysty_kod_podrecznik_dobrego_programisty_robert_c_martin,czykod.htm) w której pisze:

[quote=Clean Code by Robert C. Martin]Active Record
Active Record to specjalna forma DTO. Są to struktury danych ze zmiennymi publicznymi (lub dostępnymi w stylu bean), ale dodatkowo mają metody nawigacyjne takie jak save i find, Zwykle obiekty Active Record są bezpośrdenim tłumaczeniem z tabel bazy danych lub innych źródeł.

Niestety, często zdarza się, że programiści traktują te struktury danych, jakby były pełnoprawnymi obiektami, i umieszczają w nich metody zasad biznesowych. Jest to dziwne, ponieważ powoduje hybrydy struktury danych i obiektu.

Rozwiązaniem jest oczywiście traktowanie Active Record jako struktury danych i tworzenie osobnych obiektów, które zawierają logikę biznesową i ukrywają ich dane wewnętrzne (którymi będą prawdopodobnie obiekty Active Record).[/quote]
Czy to dlatego Javowy Spring, czy bazujący na nim Grails mająś dodatkową warstwę (zwaną najczęściej service) dla logiki biznesowej?

Czy to dobre podejście, czy może tylko wymysły R. C. Martina? :wink:

Czy w takim razie Rails nie powinno zyskać dodatkowej standardowej warstwy (oprócz models, views, conrollers jeszcze jeden folder services) na logikę biznesową?

PS.
Tak dla ścisłości:
DTO: http://en.wikipedia.org/wiki/Data_Transfer_Object
Zacytowany fragment pochodzi z podrozdziału ActiveRecord z rozdziału 6: Obiekty i struktury danych

Dobre pytanie. W zasadzie można by tutaj trochę pofilozofować, czemu ActiveRecord (w sensie wzorca programowania, tzn. jako DAO/DTO) powinien spełniać tylko to zadanie. W Railsach (tzn. w Railsowym ActiveRecordzie) wyglądałoby to mniej więcej tak, że byłaby klasa:

class Invoice < ActiveRecord::Base has_many :items end
i nic więcej. W Javie i innych językach jej podobnych, klasa ta byłaby oczywiście bardziej rozbudowana. Podobnie DataMaper, czy Djangowy Model w powyższej definicji umieszcza już informacje o polach, więc jest to bliższe klasycznemu DTO/DAO.

Myślę, że brak potrzeby wprowadzania dodatkowej warstwy w Rails uzasadniony jest tym, że dzięki konwencjom, nie trzeba nigdzie specyfikować
(znacznej części) struktury takiego DAO, dlatego można integrować go z logiką biznesową. Osobiście uważam, że jest to istotna zaleta rozwiązania Railsowego, ale mam świadomość tego, że wielu teoretyków inżynierii oprogramowania uważa inaczej. Uważam jednak, że brzytwa Ockhama to jedno z najlepszych narzędzi w tej dziedzinie. Less i more.

krzyczak: Generalnie zgodnie z modelem obiektowym można (a nawet należy) łączyć struktury danych z metodami na nimi operującymi. Tak więc generalnie dopóki metody w modelu zajmują się przetwarzaniem i modyfikowaniem tychże obiektów (plus ewentualnie bezpośrednie relacje) wszytko jest w jak najlepszym porządku.
Jeżeli mamy jakąś naprawdę skomplikowaną logikę biznesową, operującą na szerokiej gamie tabel, należało by stworzyć dodatkową warstwę abstrakcji, korzystając z wzorca Prezenter lub Kompozyt, ewentualnie wykorzystać kontroler (który w gruncie rzeczy pełni rolę kompozytu, na co niektórzy patrzą krzywym okiem).

No i chyba trzeba pamietac o tym ze skomplikowana logika biznesowa nie byla targetem rails… w zamysle autorow nigdy. Zaraz mnie pewnie ktos ustrzeli :wink: ale Rails powstalo aby webaplikacje pisalo sie latwo i przyjemnie. A rozwiazywanie problemow enterprise wyszlo jakos tak po drodze (o ile wogole wyszlo;) Dlatego imho tak wiele rzeczy mamy w ActiveRecord naraz i tak dlugo musielismy czekac na ActiveModel. Ja do tej pory mam koszmarne dziesiatki linii walidacji i STI aby utrzymac logike biznesowa w ryzach starych wersji rails.

Zasada ciekawa. Wcześniej jej nie znałem. Gdyby jednak tylką nią się kierować, to cała książka C. Martina byłaby bez sensu, gdyż prawie wszystko o czym mówi powoduje powstawanie nowych bytów… :slight_smile:
Widać trzeba wszystkiego używać z umiarem…

No i generalnie chyba rzeczywiście w Railsach jest ok tak jak jest… :slight_smile:

Zasada Ockhama brzmi: “nie należy mnożyć bytów ponad (niezbędną) potrzebę”. To nie znaczy, że nie należy w ogóle wprowadzać nowych pojęć, ale więcej uwagi poświęcić na uzasadnienie ich wprowadzenia, niż zrezygnowania z nich. Jeśli nie mamy silnego przekonania, że dane pojęcie jest niezbędne, powinniśmy z niego zrezygnować.
Niestety (przechodząc do dziedziny informatyki) w świecie Javy i ogólnie języków silnie typizowanych, często nie mamy wyboru, bo konieczność wprowadzenia nowych pojęć, często wymusza sam język (niestety). Ale każdy system, które zawiera mniej pojęć, jest łatwiejszy do zrozumienia i pielęgnacji, stąd moja awersja do rozbudowanych teorii w dziedzinie inżynierii oprogramowania.

A teraz mały offtopic

krzyczak: polecasz tą książkę? czy warto ją kupić?

Czy mógłbyś rozwinąć myśl i napisać w którym miejscu ActiveRecord wpływa na modelowanie logiki biznesowej i jak ActiveModel ma pomóc w tym temacie?

Od siebie wrzucę linka do fajnego postu o tym jak można trochę uszczuplić modele i lepiej podzielić logikę biznesową: http://www.engineyard.com/blog/2010/let-them-code-cake/

Byc moze jestem niedouczony, ale w Rails najwiecej z logiki biznesowej zapniesz w ActiveRecord: Validation, State Machine, Observing, Callbacks. Bez ActiveModel jestes zawsze zmuszony do tworzenia tabeli, migrowania jej w trakcie rozowju etc, a nie zawsze jest to potrzebne.

Ale jesli to wszystko co wymienilem po oprogramowaniu nie zlozy sie na logike biznesowa, to prosze, uswiadomcie mnie co to jest ta logika biznesowa i gdzie ona w takim wypadku sie w Rails bedzie znajdowala :wink: cale zycie bylem uczony Thin Controller Fat model … a teraz chyba czuje ze odrkyje jakis nowy paradygmat w Rails.

Nie, no thin controller, fat model to podstawa. Ale przy tym wyciągnięcie jak najwięcej logiki do walidatorów, observerów czy po prostu modułów i klas bazowych dziedziczących, potencjalnie wspólnych dla wielu modeli to dobry pomysł.

Również warto tworzyć własne klasy dlazadań specjalnych typu opakowywanie web services, generowanie grafiki, pdfów itd, nie musi to siedzieć w modelach.

[quote=hubertlepicki]Nie, no thin controller, fat model to podstawa. Ale przy tym wyciągnięcie jak najwięcej logiki do walidatorów, observerów czy po prostu modułów i klas bazowych dziedziczących, potencjalnie wspólnych dla wielu modeli to dobry pomysł.

Również warto tworzyć własne klasy dlazadań specjalnych typu opakowywanie web services, generowanie grafiki, pdfów itd, nie musi to siedzieć w modelach.[/quote]
Dokładnie i własnie dlatego ActiveModel to coś czego mi brakowało kilka lat temu :wink: i obecnie mam 2 aplikacje po > 140 modelów

[quote=Paweł Kondzior]Byc moze jestem niedouczony, ale w Rails najwiecej z logiki biznesowej zapniesz w ActiveRecord: Validation, State Machine, Observing, Callbacks. Bez ActiveModel jestes zawsze zmuszony do tworzenia tabeli, migrowania jej w trakcie rozowju etc, a nie zawsze jest to potrzebne.

Ale jesli to wszystko co wymienilem po oprogramowaniu nie zlozy sie na logike biznesowa, to prosze, uswiadomcie mnie co to jest ta logika biznesowa i gdzie ona w takim wypadku sie w Rails bedzie znajdowala :wink: cale zycie bylem uczony Thin Controller Fat model … a teraz chyba czuje ze odrkyje jakis nowy paradygmat w Rails.[/quote]
Bez przesady, ja po prostu pytam, nie chcę tutaj formułować jakichś nowych paradygmatów.

Po prostu to o czym piszesz od dawna jest dostępne w wielu pluginach niezależnych od active recordu. Oczywiście, ActiveModel to spina w jedną całość i ułatwia niesamowicie sprawę, ale railsy nie zmuszają przecież do tego, żeby wszystkie modele były spięte z bazą danych ;]

[quote=drogus]Bez przesady, ja po prostu pytam, nie chcę tutaj formułować jakichś nowych paradygmatów.

Po prostu to o czym piszesz od dawna jest dostępne w wielu pluginach niezależnych od active recordu. Oczywiście, ActiveModel to spina w jedną całość i ułatwia niesamowicie sprawę, ale railsy nie zmuszają przecież do tego, żeby wszystkie modele były spięte z bazą danych ;][/quote]
Jasne ze jest dostepne, ale jednoczesnie jest wszystko juz bezposrednio w Rails bez ActiveModel trzeba bylo wszystko reimplementowac

[quote=seeweer]A teraz mały offtopic

krzyczak: polecasz tą książkę? czy warto ją kupić?[/quote]
Polecam jak najbardziej! Czyta się dość przyjemnie i jest ciekawa. Przedstawia wiele ciekawych praktyk. Niektóre wydają mi się dziwne, ale nie trzeba przecież dla siebie adaptować wszystkiego…
Mi polecił tę książkę kolega, kupiłem i jestem zadowolony.
Przykłady kodu są co prawda w Javie, ale nie przeszkadza to raczej w niczym…

Ok, muszę się przyznać że kupiłem ową książkę i jestem gdzieś w połowie na razie. Pozycja super!

Wracając jednak do wątku, głównego, to po przemyśleniu wszystkiego, wydaje mi się że autor ma sporo racji. Szczególnie przy zastosowaniu reguły że każda klasa powinna służyć jednemu celowi, tak samo każda funkcja. Patrzę na moje aplikacje i widzę klasy typu User, Place itd. które służą zdecydowanie większej ilości celów niż 1. i zrozumienie ich budowy i działania wymaga chwili zastanowienia.

Pakowanie logiki biznesowej do modeli sprawdza się świetnie, ale co zrobić jeśli plik klasy ma już 1000 linii i zapowiada się że nadal będzie rósł? W sumie niezależnie od powyżej wymienionej książki, ostatnio zaczęłem pakować coraz więcej logiki do oddzielnych klas, modeli bez tabel czy też klas użytkowych. Przykładowo, klasa implementująca łączenie dwóch rekordów w tabeli w jeden (Record::Merger.create(model1, model2)) czy głosowanie (Vote.create(user, resource, how_voted)) itd, właśnie aby odchudzić modele ActiveRecord.

Zastanawiam się teraz czy nie można by pójść o krok dalej - potraktować modele ActiveRecord jedynie jako warstwę pośredniczącą bazą danych a aplikacją, a całą logikę biznesową implementować w oddzielnych, wyspecjalizowanych klasach. Myślę że ActiveModel był by często bardzo przydatny do tego typu zastosowań.

Ciekaw jestem czy ktoś próbował stosować tego typu rozwiązania w RoR jako paradygmat? Wydaje mi się że było by z tym więcej roboty na krótką metę, ale może warto.

W linku, który podalem powyżej jest dokładnie coś takiego.

Ja też staram się powoli rozpakowywać kawałki logiki z modeli do oddzielnych klas. Łatwiej się to testuje i można później łatwo wykorzystać w innych miejscach.

Staram się tylko uważać, żeby nie robić tego za wcześnie, to znaczy raczej zaczynam od kodu w modelu i kiedy widzę, że nadaje się do wypakowania w inne miejsce, to coś z tym robię.

Wydaje mi się że życie programistów ruby zabardzo ich rozpieszcza :wink: 1000 linii modelu ? Co mają mówić programiści innych języków ? Czy 100 linii logicznie poukładanej klasy to naprawde tak dużo ? :stuck_out_tongue:

1000 linii to dużo. Jakieś 500 za dużo jak na mój gust, ciężko się w tym połapać już…

W książce Enterprise Rails (http://www.amazon.com/Enterprise-Rails-Dan-Chak/dp/0596515200 , http://helion.pl/ksiazki/rails_projektowanie_systemow_klasy_enterprise_dan_chak,raprsy.htm) active recod jest traktowany tylko jako warstwa fizyczna, nad którą jest zbudowana warstwa biznesowa. Jestem ciekaw jak często pisaliście aplikacje tak duże, że warto było coś takiego uczynić?

Inna sprawa, że w takim wypadku traci się chyba bardzo dużo z istniejącego ekosystemu Rails. Mnóstwo pluginów i gemów opiera o się założenie, że model, który otrzyma jest wydziedziczony z AR::Base. Może w po Rails3 się to zmieni nieco i będą tylko oczekiwać czegoś zgodnego z ActiveModel. Wtedy taki podział byłby pewnie łatwiejszy utrzymaniu.

Ja miałem okazję dwukrotnie pracować nad projektem w którym dodatkowa warstwa abstrakcji była użyteczna.
W rails tworzenie tego typu struktur powinno być znacznie łatwiejsze, ja osobiście korzystałem z własnego mocno przerobionego plugina ActiveForm