Opisywałem ostatnio wzorzec DDD jako narzędzie dokumentowania analizy i tworzenia modeli analitycznych. Faktycznie, czytelnicy mają wiele racji twierdząc, że często jest on zbyt bliski implementacji (zbyt szczegółowy a więc trudny dla nieprogramistów). Niejednokrotnie „lepszym pomysłem” jest opis logiki systemu na nieco wyższym poziomie abstrakcji, pozostawiając tym samym więcej swobody developerowi.
Od czasu do czasu używam wzorca BCE (Boundary, Control Entity) do modelowania logiki biznesowej. Jego rodowód sięga chyba jeszcze czasów początków [[metodyki RUP]]. Pierwotnie był traktowany jako wzorzec analityczny do modelowania architektury kompatybilnej z MVC, w swej oryginalnej postaci nawiązuje do EJB/DAO ([[Enterprise JavaBeans/Data Access Object]]). Przykładowe standardowe użycia (źr. wiki):
Wzorzec BCE kojarzony jest głównie z architekturą EJB (Enterprise Java Beans). Od tamtej pory EJB jednak nieco odeszło w „niepamięć”, nie mamy już J2EE a JEE. Wzorzec MVC doczekał się sensownej moim zdaniem mutacji do wersji MVVMC (Model-View-ViewModel Contoler), MVP, i kilku innych odmian. Diagram w tej konwencji jest także nazywany „robustness diagram” i kojarzony jest z metodyką ICONIX, jednak osobiście polecam stosowanie zasad UML i używanie diagramu klas zgodnie z jego „zasadami” czyli używanie związków użycia (linia przerywana z grotem a nie ciągła) i uznanie, że robustness diagram to po prostu diagram klas.
Podstawowa pierwotna wersja interpretacji wzorca BCE opisana jest zwięźle tutaj:
This pattern is similar to the Model View Controller pattern (described here [BUS96] and here [WIKP-MVC] among other places), but the Entity Control Boundary pattern is not solely appropriate for dealing with user interfaces and it gives the controller a slightly different role to play. (za Entity-Control-Boundary Pattern).
Stosuję go jednak w „nieco zmienionej” formie.
Boundary, Control, Entity
Jak widać na powyższym, BCE nawiązuje bezpośrednio do wzorca MVC, klasy boundary są w tej wersji już elementami komponentu View. Starając się oddzielić (hermetyzować) logikę biznesową od części „niebiznesowej” kodu, będącej w większości przypadków elementami środowiska ([[framework]]) aplikacji.
Nieco inne podejście, to które stosuję obecnie, opisuję poniżej. Zachowując podstawowe znaczenia tych trzech klas (BCE), nawiązałem do wzorca MVVM. Powstaje więc konstrukcja, w której Model ma strukturę M-VM, pozostałe elementy View i Controler mają konkretne zadania:
- M-VM to struktura części dziedzinowej (Model-ViewModel),
- View – tradycyjnie odpowiada za prezentację,
- Controler – tradycyjnie odpowiada za zarządzanie całością, w tym poza-funkcjonalne wymagania (np. kodowanie transferu danych nie jest elementem dziedziny systemu, itp.)
Pewna ciekawostką jest w tym wzorcu właśnie element VM (ViewModel). Jego idea polega na umieszczeniu w obszarze dziedziny dodatkowej wiedzy (logiki), sterującej tym jakie (chodzi o ich „bogactwo”) informacje są podawane na urządzenia prezentujące o różnych możliwościach np. mały lub duży ekran, rozdzielczość czy wręcz komunikacja z pomocą SMS.
Powyższy diagram pokazuje schemat budowania modelu na bazie tego wzorca. Jak widać element dziedziny Model zachowuje się zawsze tak samo, reprezentuje wiedzę i logikę dziedzinową (np. komplet informacji o osobie), Dziedzina (Model) jest dodatkowo wyposażona w wiedzę o możliwościach prezentowania pełnej lub okrojonej wersji wiedzy publikowanej przez obiekt dziedzinowy (klasy ViewModel). Dzięki temu View nie zawiera żadnej logiki np. filtrowania pełnej informacji, dostaje tylko prosty przygotowany zestaw danych do prezentacji poza samą prezentacją (dla uproszczenia pomięto komponent Controller).
Jest to o tyle wygodne i ważne, że stosowanie wzorca BCE wyłącznie do modelowania logiki biznesowej wymaga zachowania hermetyzacji komponentu Model. Spotkać się można z diagramami, w których boundary będzie elementem komponentu Model (jako ViewModel). Jego rola to stworzenie dedykowanego interfejsu np. pomiędzy komponentem View (lub Controller) a Model. Dzięki temu możliwe jest np. stworzenie odrębnego interfejsu dla View na duży ekran i odrębnego dla View na np. małych ekranach smartfonów. Takie podejście wygląda tak:
Znaczenie (semantyka) stereotypów:
Boundary to klasa oznaczona stereotypem <<boundary>>, na diagramach reprezentowana może być ikoną jak po lewej. Odpowiedzialność klas boundary to separacja modelu dziedziny od innych komponentów (to często adapter, fasada, interfejs). Dzięki temu ewentualne zmiany Modelu nie przeniosą się na komponenty View i Controller oraz możemy zbudować np. różne dedykowane i bezpieczne dziedzinowe adaptery dostępowe do Modelu (np. w systemie nawigacyjnym boundary dostarczy Modelowi koordynaty geograficzne, View pozwoli wprowadzić je z klawiatury a Controller z odbiornika – interfejs – GPS, Model staje nieczuły na źródło tych koordynatów).
Control. Klasy oznaczone stereotypem <<control>> lub ikoną jak po lewej. Są to klasy odpowiedzialne za wewnętrzne usługi specyficzne dla dziedziny, „umiejętności”. Nie są to klasy utrwalane. Ta klasa będzie obliczała np. czas przejazdu na bazie wytyczonej w systemie nawigacji strasy.
Entity. Najbardziej kontrowersyjna klasa, w pierwotnej wersji interpretacji wzorca BCE (EJB) oznaczała tylko dane utrwalane, nawiązując do EJB stanowiła „materiał” na tak zwany [[antywzorzec obiektowy o nazwie anemiczny model dziedziny]]. Klas oznaczonych stereotypem <<entity>> używamy do modelowania wiedzy, czyli zgromadzonych danych, nie są to jednak anemiczne klasy bez operacji. Będą to np. punkty na mapie cyfrowej.
Trzy powyższe typy klas są tu elementami komponentu Model. Przejście na poziom DDD, drogę w kierunku implementacji tego modelu przy powyższych założeniach, można realizować np. tak:
Interpretacja ta pozwala zachować główny cel czyli rozłożenie logiki Modelu na „prostą” mapę trzech elementów: kontaktu z otoczeniem (boundary), realizacji logiki i reguł (control) oraz zachowywania „biernych” obiektów biznesowych (entity). Mała adaptacja konwencji do wzorca MVVM-V-C pozwala uzyskać komfort całkowitej separacji tak modelowanego Modelu od jego otoczenia.
Tak więc jest to moim zdaniem droga do modelowania wymagań metodą „tak to ma działać” a nie tylko „tak to ma wyglądać”, bo to drugie jest przyczyną wielu problemów…
Wielu developerów zaciekle broni się przed tezą, że wymagania to także „żądana realizacja logiki biznesowej”, oczekują wyłącznie zestawu: wymagania funkcjonalne i poza-funkcjonalne. Jest to moim zdaniem źródło dwóch ryzyk:
- jeżeli zamówienie jest opisem czarnej skrzynki tak na prawdę nie wiemy do dostaniemy jako jej realizację,
- jeżeli autorem realizacji jest dostawca pozostaje mu niezbywalne autorskie prawo osobiste do projektu tej realizacji (majątkowego nie musi wydać).
Dlatego warto, jako wymaganie przekazać projekt tak zwanej „białej skrzynki”, bo zabezpiecza to nas przed powyższymi ryzykami.
Literatura
Polecam ciekawy artykuł na podobny temat:
As we have seen, the fundamental idea of MVC is a separation of the domain logic and the GUI objects into the Model and the View. These two are linked indirectly by using the Publish-Subscribe mechanism known as the Observer pattern. Another element of this design is a Controller that implements a particular strategy for the View. (Model View Controller, Model View Presenter, and Model View ViewModel Design Patterns – CodeProject).
Oraz (apropo MVVM iMVC):
Warto pisać aplikacje w taki sposób, żeby można było je w całości obsługiwać za pomocą testów jednostkowych. Jak należy to rozumieć? Budując aplikację w taki sposób, że cała jej funkcjonalność dostępna jest bez interfejsu użytkownika, możesz każdy jej aspekt opisać za pomocą testów jednostkowych. To pozwala na wstępne testowanie zgodności aplikacji z wymaganiami wstępne, bo cały czas należy pamiętać, że testy jednostkowe to narzędzie, wspierające programistów, a nie testerów i jako takie nie może zastąpić testów aplikacji. (Wykorzystanie TDD wraz ze wzorcem MVVM).
Kiedy i po co robimy te modele?
Tu nawiążę do MDA (tu co nieco o Model Driven Architecture). W łańcuchu modeli MDA mamy trzy modele: CIM->PIM->PSM. Analiza biznesowa na pierwszym etapie to tworzenie modelu organizacji pomijającego istnienie jakiegokolwiek oprogramowana (CIM – [[Computation Independent Model]]), celem jest pełne zrozumienie i opisanie funkcjonowania organizacji.
Kolejny etap to opracowanie modelu dziedziny projektowanego (wymaganego) oprogramowania. To model PIM ([[Platform Independent Model]]). Jego celem jest udokumentowanie logiki oprogramowania, wymagania poza funkcjonalne są realizowane niezależnie od tej logiki. Tak więc model PIM do coś co nazywane bywa: „wymagania dziedzinowe”. Wymagania funkcjonalne nie opisują w ogóle logiki biznesowej, same przypadki użycia nie są wystarczające do napisania oprogramowania, potrzebna jest wiedza o logice biznesowej. Funkcjonalność „system sprzedaży automatycznie uwzględnia upusty dla klientów” nic nie mówi. Ale gdzie definicja logiki udzielania tych upustów? Gdzie jest wiedza o upustach a gdzie o towarach? Przy kliencie czy przy towarze? Nie wiadomo, trzeba to jakoś zrozumieć i udokumentować, w sposób pozwalający na implementacją czyli jednoznacznie. I po to się tworzy modele PIM.
Tu jednak należy się mała uwaga (bo nigdy nie mów nigdy): bywa, że ograniczenie o treści „maksymalny czas oczekiwania na ofertę nie może przekroczyć progu cierpliwości 80% klientów”. Pozostaje zbadanie „progu cierpliwości”, ten parametr nie jest elementem reguły biznesowej gdyż jest właśnie „parametrem” a nie „zasadą” (reguły biznesowe to zasady postępowania a nie reguły podejmowania decyzji czy mierniki). Ta reguła może stać się elementem dziedziny projektowanego systemu jeżeli np. jej spełnienie jest elementem budowy przewagi rynkowej. Co to znaczy? Że nie należy optymalizować obecnego modelu dziedziny (np. podnoszenie wydajności poprzez uproszczenie struktury opisu produktów) a dodać do dziedziny strukturę pozwalającą na wykonywanie pewnych wybranych czynności prościej i szybciej. To jednak temat o wzorcu projektowym CQRS ale to temat na inny artykuł.
(inne źródła:
basic rules apply: Actors can only talk to boundary objects.Figure 3. Robustness Analysis RulesBoth boundary objects and entity objects are nouns, and controllers are verbs. Nouns can’t talk to other nouns, but verbs can talk to either nouns or verbs. Boundary objects can only talk to controllers and actors. Entity objects can only talk to controllers. Controllers can talk to boundary objects and entity objects, and to other controllers, but not to actors
źr. Memorandum).
Omówiony wzorzec BCE jest bardzo podobny do tego, który wykorzystywany jest w metodzie ICONIX, która to również daje wytyczne jak separować logikę biznesową – tu również metoda pozwala zachować wyższy stopień abstrakcji niż DDD. Niemniej jednak zastosowanie DDD umożliwia dokładne odzwierciedlenie logiki biznesowej.
To prawda, w ICONIX mamy tu jednak niestandardowy diagram: Robustness Diagram (trudno go rysować i przekazywać dalej, narzędzia CASE z reguły nie wspierają go), jest to jednak chyba po protu diagram klas i opisane stereotypy… Inna sprawa, że ICONIC narzuca poprzedzanie kodowania projektowaniem 🙂
Kompromis pomiędzy BCE a DDD chyba zawsze powinien być kompromisem pomiędzy ryzykiem „zbytniego uogólnienia” a oczekiwaniem wykonawcy ;).
Pogrzebałem w ICONIX, jest dużo wspólnego… nawet zmodyfikowałem artykuł…
Czy trudno rysować diagram Robustness? Myślę że nie łatwo, podobnie zresztą jak i model dziedzinowy w metodzie DDD. Zastosowanie tej metody, podobnie jak wzorca ECB, widzę raczej jako etap pośredni przy projektowaniu modelu dziedzinowego zgodnego ze wzorcami DDD. Pytanie czy warto taki etap wykonywać? Czy wykonanie dodatkowego modelu nie wprowadzi w tym przypadku więcej zamieszania niż pożytku?
Oba diagramy to głęboka analiza i projektowanie, to nie może być łatwe :). Osobiście wydaje mi się, że trzy klocki BCE na diagramie klas to właśnie Robustness Diagram (z dokumentacji tych wzorców i metodyki na to wychodzi, BCE to stereotypy klas, a Robustness diagram to ich użycie). Co do nadmiarowości, to skłaniam się ku tezie, że przejście: analiza pojęciowa, model BCE (Robustness), model DDD i potem projekt implementacji, to naturalna droga analizy i projektowania zstępującego (od ogółu do szczegółu). Np. w pierwszej iteracji planujemy użycie usługi (control) o nazwie „tworzenie nowego dokumentu”, w drugiej iteracji, po akceptacji koncepcji, decydujemy którego wzorca kreacyjnego użyjemy w miejscy „Tworzenie nowych dokumentów”, moim zdaniem to podobnie jak dekompozycja procesów…