Mar 18 2007
Obiekty biznesowe w aplikacji.
Filed under MVC,PHP by Łukasz Dywicki
Pierwsze błędy
Pamiętam swoje pierwsze implementacje MVC, w czasach gdy słowo framework nie było jeszcze trendy a wiele osób, w tym i ja, nawet go nie używało. W owych pierwszych implementacjach MVC model był pewnego rodzaju fasadą, która zapewniała dostęp do danych.
Problem polegał na tym, że kod np klasy User wyglądał następująco:
<?php class User extends Model { function User(DB $db) { parent::Model($db); } function getById($id) { $sql = 'SELECT * FROM user WHERE user_id = '. $id; return $db->query($sql); } } ?>
Jak widać na powyższym listingu klasa jest odpowiedzialna za pobieranie danych. Problem takiej implementacji polega na tym, że wciąż operujemy na tablicach bądź na ResultSetach zwracanych przez obiekt odpowiedzialny za wykonywanie zapytań. Kod w takim wykonaniu jest mocno związany z bazą danych i jest ciężki do użycia w innych projektach. Co więcej w pierwszym podejściu nie korzystałem z wielu możliwości jakie daje programowanie obiektowe i po prostu odwołania do klasy nadrzędnej czy też metody abstrakcyjne były co najmniej sporadyczne. Także, każdy “model” miał swoją metodę getById, która miała swoje zapytanie. Jest to najprostsza implementacja, która zapewnia tylko jedno – wyłączenie zapytań do oddzielnych plików.
Wzorce
Znacznie lepszym krokiem byłoby wykorzystanie możliwości chociażby dziedziczenia i użyć nazwanego przez Martina Fowlera wzorca ActiveRecord, który zapewnia nam mapowanie pojedynczej encji do klasy.
Problem, jaki powstaje przy użyciu ActiveRecordu to niestety uzależnienie kodu od wybranej implementacji wzorca, co więcej kod będzie nieprzenośny, np nie da się go użyć bez zewnętrznych bibliotek. Zatem problem pozostaje taki jak był wcześniej – logika, która była związana z danymi (np przeliczanie wartości jednego pola w zależności od drugiego) została zawarta w modelu staje się bezużyteczna.
Problemy te można rozwiązać wyraźnie oddzielając obiekty z których korzysta aplikacja od warstwy dostępu do nich. Tutaj rodzi się pojęcie Domain Model czy też Domain Object. Przede wszystkim jest to kod, który jest niezależny od źródła danych np obiekt Invoice możemy pobrać zarówno z bazy jak i z zewnętrznego systemu powiedzmy przez usługę sieciową. Obiekt zawsze zachowa się w ten sam sposób i jego użycie nie będzie zależne od miejsca z którego je uzyskaliśmy. Idealnym przykładem implementacji Domain Modelu są np beany utrwalane przy pomocy Hibernate. Mamy kod, który jest przenośny, w którym nie ma żadnych jawnych odwołań do informacji zawartych poza “obiektami domenowymi”, jest ona po prostu autonomiczna.
Ciężko jest określić czym w pierwszej kolejności się zająć bazą czy obiektami biznesowymi? Wydaje mi się, że modelując najpierw bazę danych ciężko jest wychwycić dziedziczenia pomiędzy obiektami biznesowymi i najzwyczajniej w świecie zostaną one pominięte. Z drugiej strony tworząc najpierw obiekty biznesowe możemy natrafić na trudności z odwzorowaniem całej, nie raz, złożonej struktury do encji w bazie.
Data Access Objects
Dostęp warstwy z obiektami domenowymi realizujemy przez Data Access Objects. Jest to zespół klas, które zapewniają odczyt, usuwanie oraz zapisywanie obiektów (całość modyfikujemy przez dostępne w obiektach domenowych metody). DAO ma na celu zapewnienie jednolitego interfejsu dla dostępu do obiektów bez pojawiania się kodu specyficznego dla danego źródła danych, niezależnie czy to będzie baza danych czy usługa sieciowa.
Service Layer
Warstwa usług to miejsce przecięć różnych źródeł danych. Granica między DAO a Service Layer jest dość istotna – DAO obejmuje transakcje systemowe podczas gdy Service Layer transakcje biznesowe. Może słowo wyjaśnienia na temat tych dwóch rodzai transakcji – pierwsza jest zwykle kontrolowana przez bazę danych i zawiera ciąg poleceń SQL. Transakcja biznesowa obejmuje jedną konkretną operację np przelew bankowy – to logowanie, określenie kwoty i odbiorcy, weryfikacja kodu jednorazowego oraz zablokowanie środków. Powodzenie takiej operacji zależy od wielu czynników w tym od systemów zewnętrznych.
Warstwa usług jest bardzo przydatna w chwili gdy z tej samej logiki korzysta więcej niż jeden system. Niestety nie mam wielkiego doświadczenia z takimi sytuacjami i nie czuję się na siłach by o tym pisać, a to co tu przytoczyłem to głównie to co pamiętam z PoEAA.
Ja poproszę o wyjaśnienie lamerowi o co biega w tych Beans’ach. Co to? Jak się używa? Po co :P ?
Bean to zwykła klasa, która udostępnia metody dzięki którym możesz się dobierać do danych, które hermetyzuje. Np.
Czy jak dobrze zrozumialem fasolka ;) to kontener poprostu ze zdefiniowanymi setterami i getterami ? : )
Dokładnie, czasami z niewielką ilością logiki..
Główną zaletą EJB (Enterprise Java Bean) jest to, że można je umieścić w kontenerze, który nimi zarządza (tranzakcje, persistence. Poza J2EE na klasy tego typu mówi się po prostu Encje.
Uwaga – model domenowy, o którym mówię, to nie “Domain Model” w ujęciu Fowlera, tylko w ujęciu Unified Process.
Andrew – pojęcie “bean” w Javie jest starsze niż samo EJB i wykorzystałem to pojęcie w tej wierze (bez jakichkolwiek aluzji do serwerów aplikacyjnych). Zgodnie z definicją Suna:
JavaBean jest przenośnym, niezależnym od platformy komponentem napisanym w Javie. Specyfikacja JavaBeans umożliwia tworzenie komponentów, które będą używane wielokrotne – napisz raz, uruchamiaj wszędzie.
Samo EJB w wersji 2.x nie ma nic wspólnego z tradycyjnymi beanami, dopiero od trójki można mówić o wykorzystaniu ‘fasolek’ jako takich (JPA).
Pytanie odnośnie kolejności prac – projekt bazy czy “warstwy domenowej” – podyktowane jest trudną odpowiedzią na nie. Prawdę powiedziawszy obiekty, które będą funkcjonować w aplikacji można wskazać bez większych problemów, po prostu powstają one same na podstawie kolejnych zdań ze specyfikacji wymagań. Próbowałem kiedyś zaprojektować taką bazę aplikacji logistycznej – największym problemem było wyszukiwanie i optymalizacja tras a nie samo utrwalanie czy odwzorowanie obiektów. Mając tabelki mogłem spokojnie z nich powiedzieć jak będą wyglądały klasy i zależności między nimi. Fakt, że nie byłem w stanie powiedzieć jakie hierarchie klas będą występować.
Andew Dlaczego sądzisz, że definicja klasy Author nie pasuje do JavaBeans? Co według Ciebie jest nie tak z pozostałymi klasami?
Co do POJO. Jest to zwykły JavaBean, POJO = JavaBean – chociaż były spory czy rzeczywiście mamy tutaj do czynienia z tożsamością to nic konstruktywnego z nich nie wynikło. Samo pojęcie zaczęło funkcjonować jakiś czas przed pojawieniem się Springa i jest to odwrócenie się od ciężkich rozwiązań takich jak EJB, gdzie większość komponentów musiała implementować interfejsy, których i tak się później nie wykorzystywało.
Spróbuję podejść do projektu zgodnie z Twoimi radami, zobaczę co uda mi się uzyskać. :)
– mieć możliwość korzystania z niej poprzez graficzne środowisko (np dopasowywanie przez panel właściwości);
– obsługiwać zdarzenia;
– obsługiwać właściwości (properties);
– i inne;
(http://java.sun.com/products/javabeans/faq/faq.general.html#Q2)
POJO to marketing. Inna nazwa pojęcia, może nie zamiennik, ale z pewnością oznacza to samo. Za Wikipedią:
The term has most likely gained widespread acceptance because of the need for a common and easily understood term that contrasts with complicated object frameworks. A JavaBean is a POJO that is serializable, has a no-argument constructor, and allows access to properties using getter and setter methods.
JavaBean to zwykła klasa. Nie ma w niej żadnych cudów uniemożliwiających przeniesienia pojęcia do innych języków, konstrukcji czy zachowań specyficznych tylko dla Javy. W praktyce poprawny bean zaczyna się od metod get/set.
Samo pojęcie “fasoli” ewoluowało i już dawno wykroczyło poza erę Swingowego GUI, czego przykładem może być chociażby POJO. Można bez problemów tworzyć beany znacznie prostsze. Jeśli nie korzystasz z WYSYWG-a to nie potrzebujesz PropertyDescriptora, PropertyChangeListener jest przeznaczony dla Ciebie i jeśli nie widzisz potrzeby nie musisz propagować informacji o wszystkich zmianach.
Zwróć uwagę, że pojęcia JavaBean używa się głównie na płaszczyźnie par metod get+set. JSP i JSTL bazuje na tej specyfikacji, ale nie wymaga nic poza odpowiednimi metodami.
DTO czy też encja również jest JavaBeanem.
Wszystkie pojęcia ewoluują. Pierwotnie specyfikacja JavaBeans (1997 rok) tyczyła się tworzenia graficznego interfejsu użytkownika z biegiem czasu, gdy pojawiło się większe grono technologii pojęcie beanów zaczęto stosować szerzej – gdzie nie ma konieczności tworzenia dodatkowych elementów, poza określoną konstrukcją klas. Jeśli nie możesz znieść stosowania takiego poglądu mów sobie na przykładowe klasy powiedzmy – fasole. Tworzone na bazie specyfikacji JavaBeans.
Słuchaj Andrew, czepiłeś się tych beanów jak rzep psiego ogona. Mam tego serdecznie dosyć.
Gwoli ścisłości – to Ty zacząłeś od pomylenia JavaBeana z EJB. Następnie dowiadujesz się że takie pojęcie istnieje i stwierdzasz, że moje przykłady są złe, po czym wędrujesz do specyfikacji z 1997 roku i mówisz mi, że beanem jest tyko to, co spełnia wymogi specyfikacji o której, nota bene, nawet nie wiedziałeś. I mimo wszystko dalej śmiesz mnie w tej kwestii pouczać?
Nie zamierzam zmieniać znaczenia beana, ponieważ większość znanych mi programistów Javy tak właśnie to pojęcie rozumie. To po prostu klasa z metodami zgodnymi z konwencją i bezargumentowym konstruktorem, stąd tożsamość encji, POJO i DTO. I takie postrzeganie tego pojęcia jest przenośne na dowolny grunt. Bean nie musi mieć przecież żadnych dodatkowych elementów. Jeśli tego nie rozumiesz to potraktuj to co napisałem parę zdań wcześniej jako najmniejszy wspólny mianownik.
Nie zamierzam zmieniać znaczenia, jak to określiłeś, “fanie brzmiącego terminu” ponieważ posługuję się synonimem utartym w branży a nie przestarzałym dokumentem, stąd moje wytłumaczenie pojęcia Strzałkowi, nawet jeśli nie pełne, na pewno nie odbyło się z krzywdą dla niego samego.
Z mojej strony, nalegam na zakończenie tematu, ponieważ te wywody do niczego nie prowadzą.
—
*** Wszystko po “—” możesz usunąć żeby nie zaśmiecać ci bloga, nie chciałem ciebie obrazić, ani nic w tym sensie, tylko wyprostować pewne terminy.
Andrew daruj sobie wycieczki osobiste, prosiłem o to by zakończyć tą dyskusję! Doszliśmy do porozumienia w kwestii kolejności podejmowania prac przy projektowaniu, co mnie bardzo cieszy. Niech spór tyczący się pojęcia “beana” pozostanie więc otwarty. Nie zamierzam prosić kolegów o to by opisywali w jaki sposób rozumieją “beany” ponieważ jest to irracjonalne i na pewno nie będzie dla Ciebie wiarygodne.
Wszystko co miało sens zostało już powiedziane. Osoby, które trafią do tych komentarzy same będą musiały rozsądzić którą wersję pojęcia akceptują a którą odrzucają.
Czytam i czytam wasze komentarze. Myślę, że warto by było zakończyć tę dyskusję, bo powoli zaczyna zmierzać w złą stronę.
Przypadkowo trafiłem na wpis na blogu innej osoby traktujący po części o JavaBeans..