Pojęcie aktora, integracji, interfejsów itp. stale się przewija w dokumentach wymagań, niejednokrotnie w sposób błędny co wprowadza zamęt w dokumentacji i zamęt w zrozumieniu problemu do rozwiązania.
Co znajdziemy w dokumentacji UML (UML 2.4.1. Superstructure 2011-08-05, rozdz. 16):
Use cases are a means for specifying required usages of a system. Typically, they are used to capture the requirements of a system, that is, what a system is supposed to do. The key concepts associated with use cases are actors, use cases, and the subject. The subject is the system under consideration to which the use cases apply. The users and any other systems that may interact with the subject are represented as actors. Actors always model entities that are outside the system. The required behavior of the subject is specified by one or more use cases, which are defined according to the needs of actors.
Ogólnie: Przypadek Użycia oznacza konkretne (w określonym celu) użycie Systemu. Standardowo Przypadków Użycia używa się do opisania wymagań wobec przyszłego Systemu. Kluczowe pojęcia skojarzone z Przypadkami Użycia to Aktor i przedmiot opisu czyli System. Użytkownik lub inny system może wchodzić w interakcje z Systemem jako Aktor. Aktor to zawsze byt zewnętrzny względem Systemu (ale nie każdy nam znany). Wymagane zachowania (reakcje) Systemu są specyfikowane z pomocą jednego lub wielu przypadków użycia reprezentujących potrzeby Aktorów.
Aktorem jest więc zewnętrzny byt, który ma potrzebę użycia Systemu w konkretnym celu. Innymi słowy: Przypadek Użycia opisuje usługą świadczoną przez System dla jego Otoczenia. Użytkownicy tych usług to Aktorzy. Tak więc generalnie Przypadki Użycia to usługi Systemu jakie świadczy i są one inicjowane przez jego Aktorów czyli żądających obsługi. Kluczowe tu jest stwierdzenie: Aktor to ten, który inicjuje dialog z Systemem żądając obsługi.
Aby pokazać gdzie są problemy pokaże to dokładnie od końca. Jak ktoś nie jest analitykiem może od razu przejść na koniec :).
Integracja czyli komponenty i podsystemy
Powyżej mamy dość typową sytuację, w której mamy System projektowany, inny Zewnętrzny System 1, który już u nas funkcjonuje. Z systemu projektowanego będzie korzystał Użytkownik oraz kolejny inny Zewnętrzny System 2. Zewnętrzny oznacza tu “już istniejący” (zewnętrzny względem projektowanego).
Na diagramie (UML, diagram komponentów) użyto symboli komponent i interfejs. Warto wiedzieć, że interfejsy mają “dwa oblicza”. Co do zasady w UML (i metodach obiektowych) występuje pojęcie “użycia” to znaczy, że “coś żąda realizacji usługi od czegoś” albo jak kto woli “coś używa czegoś do realizacji swoich zadań”. Dlatego interfejsy dzielimy na oferowane i wymagane. Użytkownik używa Systemu (jest on dla niego narzędziem pracy) do realizacji swoich celów. Może to także robić (żądać obsługi) inny System.
Dwa typy interfejsów
Interfejs oferowany i wymagany to dwa różne “byty”. Ogólnie, jeżeli jeden system wysyła żądanie wykonania jakiejś usługi do drugiego, to pierwszy ma interfejs wymagany a drugi oferowany (i muszą być zgodne ;)). Zestaw poleceń “rozumianych” przez interfejs oferowany to “język”, który musi znać interfejs wymagany (system wywołujący). W praktyce wygląda to tak, że interfejs oferowany ma dokumentację, którą powinien dostać programista interfejsu wymaganego (wywołującego zdalny System).
Tak więc mamy już świadomość ról używający i używany (w UML pokazuje to związek użycia: strzałka z linii kreskowej z grotem wskazującym na system/obiekt wywoływany, świadczący usługę, patrz pierwszy diagram).
Od środka
Popatrzmy na “stworzony system”. System już po “napisaniu” mógł by wyglądać tak (użyto [[wzorców projektowych, w tym wzorca MVC]]):
Co tu mamy? Użytkownik to osoba korzystająca z Systemu Projektowanego. Każdy przypadek użycia ma swoją dedykowaną klasę, która “wie jak świadczyć daną usługę”. Model dziedziny odwzorowuje przetwarzane dane (Jakaś klasa, agregaty itp.), są klasy świadczące jakieś wewnętrzne usługi. W trakcie analizy i projektowania może się okazać, że jakaś potrzebna wewnątrz Systemu Projektowanego usługa jest już dostępna w sieci Zamawiającego, znamy Zewnętrzny System 1, który ją ma, sprawdzamy czy System ten ma interfejs do tej usługi lub czy można go napisać. (np. mamy już system kadrowo-placowy więc nie musimy po raz drugi implementować listy pracowników, możemy o dane pracownika prosić ten istniejący już zewnętrzny system).
W ramach projektu więc nie implementujemy drugi raz takiej usług (nie dublujemy jej w naszej sieci) tylko tworzymy interfejs, klasę która będzie wywoływała Zewnętrzny System 1, za każdym razem gdy usługa ta będzie wymagana. Nie pozwalamy na bezpośrednie wywołania z wnętrza naszego Systemu Projektowanego, bo to uzależni całe jego wnętrze od szczegółów obcego Systemu. Tworzymy “adapter” czyli “maskujemy” obcy system (Klasa <<Interfejs>> to ten adapter). Dzięki temu jego, Zewnętrznego Systemu 1, ewentualna wymiana nie zaowocuje przeróbkami naszego Systemu Projektowanego. Gdyby trzeba było zrezygnować z Zewnętrznego Systemu 1, klasę adaptera wypełnimy implementacją potrzebnych funkcji a reszta Systemu Projektowanego nie odczuje zmiany.
Mamy więc już projekt. A teraz wracamy do początku i narysujemy
Diagram przypadków użycia
który obrazuje powyższy projekt:
To jest to co zrozumie Klient, najprostszy diagram do pokazania, zawiera zakres projektu, granice systemu (najważniejsza rzecz) i aktorów (część różowa tylko dla lepszego zrozumienia treści artykułu). Inny System, jeżeli inicjuje akcję czyli żąda usługi także jest aktorem, ale System realizujący na nasze zlecenie jakieś usługi nie jest aktorem, jest tylko wywoływany, sam z siebie nie inicjuje żadnej akcji. On realizuje jakąś potrzebną nam funkcję.
Może się okazać, że Zewnętrzny system ma zarówno interfejs oferowany jak i wymagany, wtedy w projekcie, na diagramie przypadków użycia, warto operować nie nazwą zewnętrznego systemu (np. ERP który może mieć dziesiątki interfejsów oferowanych i wymaganych) a nazwą usługi (interfejs oferowany) lub zdarzenia (interfejs wymagany) będących przedmiotem takich akcji i zakresu projektu.
Diagram przypadków użycia to pierwszy test zrozumienia tego co i po co ma powstać. Jest to bardzo prosty diagram i dlatego każde uproszczenie lub niezrozumienie, prowadzi nie raz do poważnych nieporozumień a bywa, że do krachu projektu. To bardzo ważny diagram. Powie nam do czego Systemy Projektowany będzie używany, to cel sponsora. Na tej podstawie można wybrać gotowe oprogramowanie i testując je ocenić przydatność (nie radze wybierać gotowego oprogramowania na bazie prezentacji!). Ale diagram ten nigdy nie powie jaką logikę należy zaimplementować, dlatego w przypadku tworzenia oprogramowania konieczny jest jego projekt: model dziedziny systemu, jako kolejny etap projektu na oprogramowanie dedykowane. Przypomnę także, że przypadki użycia w wersji minimalnej powinny mieć opis:
- cel jego użycia (co Aktor chce osiągnąć)
- stan początkowy (wejście, dane wejściowe, itp.)
- stan końcowy (wyjście, dane wyjściowe itp.)
Tu zwracam uwagę, że powyższe to zarazem testy akceptacyjne otrzymanego oprogramowania.
System 1 równie dobrze może być aktorem, którego na diagramie przypadków użycia połączymy z PU relacją zależności – tzn wskażemy, że sposób realizacji przypadku użycia zależy od innego systemu.
Mam też wątpliwość odnośnie umiejscowienia klasy interfejsu do systemu_1 w warstwie modelu. To chyba trochę proteza. Moim zdaniem interfejs to winien w swojej warstwie widoku udostępniać System 1, my zaś z poziomu modelu w klasach realizujących funkcjonalności wymagające współpracy z systemem 1 powinniśmy za pomocą kontrolera wywoływać odpowiednie klasy interfejsu systemu 1. Jak znajdę chwilę, to uzupełnię niniejszy komentarz o stosowne diagramy.
Pokazanie na diagramie Systemu 1 widać na ostatnim diagramie (można tak narysować, rzadko się to jednak robi), jednak on nie inicjuje akcji (nie żąda obsługi) więc semantycznie nie jest aktorem, jest zewnętrzną implementacją wewnętrznej wymaganej usługi. Prostym testem jest fakt napisania własnej implementacji tego interfejsu, diagram przypadków użycia nie powinien ulec zmianie bo zakres funkcjonalny systemu nie uległ zmianie.
Diagramy te powstały na bazie prostego założenia: usługi to interfejs a jego implementacja “leży” w “naszym” systemie lub jest “na zewnątrz”. Czyli jeżeli System projektowany w swoim wnętrzu wymaga wiedzy o pracownikach to buduję klasę z taka usługą jako element domeny. Rzeczą wtórną jest to, czy ta klasa ma implementację rejestru pracowników lokalnie czy jest jedynie adapterem do zewnętrznej aplikacji (potocznie interfejs do innego programu) dostęp do danych pracownika zawsze jest elementem dziedziny tego systemu (na tym polega tu hermetyzacja implementacji rejestru pracowników). Reszta Systemu “nie ma o tym pojęcia” a decyzję o użyciu COTS podejmujemy (możemy podjąć) później. To jest moim zdaniem “piękne” w metodach obiektowych, że można opracować projekt logiki systemu nawet w 100% na poziomie interfejsów czyli opracować projekt abstrakcyjny, a potem martwić się o implementację czyli szukać gotowców na rynku zanim podejmiemy decyzje o samodzielnym kodowaniu.
Zastanawiam się czy można wykorzystać wzorzec MVC do modelowania systemów opartych na paradygmacie SOA? Jak wyglądałby wówczas jakiś prosty przypadek użycia np. systemu służącego do webowego podejmowania decyzji w oparciu o głosowanie (przykładowe usługi: rejestracja głosującego, zgłaszanie tematów, sprawdzenie poprawności oddawania głosów itp.)
Model przypadków użycia zawiera “prostokąt” System (w UML jest to “subject” czyli przedmiot modelowany) i na tym poziomie jest to czarna skrzynka. Projektowanie systemów i ich integracji, zaczynając je od Przypadków Użycia i ich Aktorów w szczególności, wymaga właśnie zrozumienia roli każdego “integrowanego” podsystemu. Należy “rozebrać” je na usługi świadczone i żądane. Szyna ESB jest jedynie implementacją narzędzia do przesyłania żądań.
Co do MVC zaś, to jest to już “wnętrze” naszej czarnej skrzynki i nie ma tu znaczenia co jest integrowane, ESB to systemy wymiany komunikatów i nic więcej. MVC w sytuacji gdy aktorami są inne systemu to głównie Model (kanoniczny model danych całości) oraz Controller czyli obsługa interfejsów.
Moim zdaniem, zgodnie ze specyfikacją UML, System 1 może być aktorem(nieinicjującym, a wspierającym realizacje przypadku użycia).
Zgodnie z dokumentacją UML 2.4.1 (strona 622):
“Each use case specifies some behavior, possibly including variants, that the subject can perform in collaboration with one or more actors.”
“These behaviors, involving interactions between the actor and the subject, may result in changes to the state of the subject and communications with its environment.
Przykład: model PU dla bankomatu z tej dokumentacji przedstawia bank jako właśnie takiego aktora.
To tak, w nawiązaniu do specyfikacji UML. Nie, neguje proponowanego tu sposobu opisu interfejsów, wydaje się dość przekonywujące.
Bank jest aktorem bankomatu bo inicjuje nie tylko wkładanie kasy ale i raporty itp… Po drugie zmiana stanu w Systemie nie wydarzy się sama z siebie, System reaguje na bodźce a bodźce generuje Aktor, nie wprost ale z tego także opisu można wyprowadzić wniosek, że Aktor to “coś” co inicjuje zachowania Systemu.
Pojęcie “Aktor supportujący” jest poza UML, bezpieczniej jest użyć pojęcia “implementacja zewnętrzna” jakiejś wymaganej funkcji. Po drugie jak napisać scenariusz dla przypadku użycia inicjowanego przez tworzony System? System świadczy usługi na żądanie z otoczenia… sam z siebie nic nie zrobi. Analogicznie (jest to konsekwencja) do interfejsu oferowanego i wymaganego, aktor korzysta wyłącznie z oferowanego.
W moich oczach restrykcyjne podejście do definicji Aktora pozwala uniknąć nieporozumień między innymi na poziomie interfejsów. Bardzo pomaga pilnowanie zakresu projektu, “prostokąt” System to granice zakresu, w konsekwencji Aktor jest zawsze poza zakresem a wykonawca ma prawo żądać dokumentacji Aktora: raz jest to opis co potrafi użytkownik (w swoich projektach wymuszam specyfikowanie tego jaka wiedzą i umiejętnościami dysponuje Aktor-Użytkownik) innym razem specyfikacja API innego integrowanego Systemu. Wykonawca Systemu, Aktorów dostaje “na tacy” oraz definiuje lub implementuje interfejsy do innych systemów zależnie od tego czy są wymagane czy oferowane. To pomaga panować na tym kto i co dostarcza.
Wracają do przypadku użycia opisującego wypłatę gotówki z bankomatu. Aktorem inicjującym jest tu klient. Aktorem “supportującym” jest system bankowy, wspierający realizację przypadku użycia, np: weryfikacje wprowadzonego kodu PIN.
W związku z tym na diagramie przypadek użycia powiązany będzie relacją z klientem (aktor inicjujący) i system bankowym (aktor “supportujący”).
Szczegóły komunikacji związanej z weryfikacją kodu PIN są ujęte w treści przypadku użycia. Ten sam system bankowy może w innej sytuacji pełnić rolę aktora inicjującego np: pobieranie raportów z bankomatu.
Wydaje się że, aktor “supportujący” często pojawia się w literaturze związanej z przypadkami użycia, więc może z “automatu” przyjąłem, że UML wspiera pojęcie aktora “supportującego”.
Cytując fragment specyfikacji UML założyłem, że ta komunikacja z otoczeniem powinna być przedstawiona za pomocą aktora “supportującego”.
Dodam jeszcze że, nie jestem zwolennikiem inicjowania przypadków użycia wewnątrz systemu, przez zdarzenia czasowe itp.
Rozumiem że, w proponowanym podejściu na diagramach nie pojawiają się aktorzy “supportujący”, a usługi zewnętrzne są dokumentowana za pomocą interfejsów wymaganego i oferowanego.
Bankomat nie jest samowystarczalny więc nie istnieje System Bankomat tylko moduł systemu bankowego służący do wypłacania gotówki z własnego konta zarządzanego Systemem Bankowym. Tu jednak zaznaczam, ze posługuje się “zasadami” ogólnej teorii systemów, która rygorystycznie traktuje pojęcie systemu i jego granic. Z drugiej strony Bankomat uznany jako “System” (podsystem większej całości) ma w sobie interfejs wymagany do Systemu Bankowego implementującego “wiedzę o moim stanie rachunku” potrzebnej do sprawdzenia ile wolno mi wypłacić (granice systemu są względne:każdy system może być podsystemem systemu nadrzędnego i odwrotnie) .
Niewątpliwie jest to kwestia przyjętej konwencji, ja przyjmuje tę “naukową” (metoda naukowa) a w ramach pracy naukowej “porządkuję” ogólniki, tu w UML (ta notacja, jak każda inna, do wykonania konkretnego modelu wymaga stworzenia pragmatyki, czasem w postaci tak zwanego profilu). Ja staram się restrykcyjnie zachowywać w modelach reguły przestrzeni nazw, to jest nie nadaje pojęciom UML “dodatkowych” znaczeń rozszerzających, raczej szukam powodu dla którego “coś nie pasuje” i z reguły okazuje się, że problem tkwi nie w “brakach UML” a w zrozumieniu problemu.
Faktem jest, że literatura jest “różna”, po wielu przeczytanych książkach z tej dziedziny widzę, że wielu autorów książek i metodyk “naciąga” na własną potrzebę znaczenia pojęć, skutki są takie, że prowadzi to nie raz do wieloznaczności “dokumentacji”… czego ja akurat “nie lubię” 😉 I faktycznie w “promowanym” przeze mnie podejściu ściśle rozróżniam np. Aktora Systemu od zewnętrznej (dostępnej nam) implementacji potrzebnych funkcji tworzonego Systemu.
Dla przykładu jednym z autorów, których “nie lubię” jest A.Cockbourn i jego książka o przypadkach użycia (jest on w wielu kręgach uznawany za guru use casów).
P.S.
W tej konwencji interfejs oferowany jest właśnie dla Aktorów, interfejs wymagany dla systemów “supportujących” ;), ten sam “duży system” (np. Bankowy) może być zależnie od kontekstu aktorem lub wsparciem zależnie od kontekstu i dobrze jest te konteksty odkryć bo pozwala to lepiej projektować te Systemy, w szczególności odpowiedzialności klas z jakich się składają (mowa o obiektowym paradygmacie, analiza i projektowanie poprzez odpowiedzialność klas to kolejna konwencja którą “lubię” 😉 ).
Dodam tylko, że lepszą metaforą niż MVC może być architektura Ports & Adapters (dawniej Hexagonal). Mamy w niej porty (interfejsy oferowane i wejściowe) oraz adaptery do tych interfejsów.
Porty oferowane pozwalają być “SOA Ready” oraz wspierają modny ostatnio “multiscreen”. Natomiast adaptery są już szczegółem “protokołu komunikacyjnego” – może być to web servis, rest, a w szczególności listener nasłuchujący zdarzeń z innego hexagonu.
W centrum hexagonu rezyduje Mico Kernej, który chroni domenę – oczywiście w stylu DDD:]
Patrze na ten hexagonal i jakoś nie czuję tego pomysłu :)…
Panie Jarosławie,
Zgadzając się z Panem co do propozycji rozwiązania jednocześnie uważam, że argumentacja typu “posługuję się ogólną teorią systemów”, “rygorystycznie traktuję pojęcie systemu”, “ja przyjmuję metodę naukową” (co sugeruje, że są jakieś inne,nienaukowe, ergo gorsze?) jest troszkę nie na miejscu i pachnie bufonadą (bez urazy).
Żadna teoria systemów nie zawiera informacji o tym, czy system bankomatowy jest samowystarczalny czy nie i czy ma być traktowany jako część całości czy też równorzędna całość. Inaczej bowiem może wyglądać model jeśli np. bankomat jest zarządzany przez niezależnego brokera, współpracującego z wieloma bankami, a inaczej jeśli jest to bankomat konkretnego banku. Nie wiedząc tego nie dezawuowałbym od razu innych pomysłów na rozwiązanie problemu.
Inna rzecz to taka, że widzę w artykule lekką niekonsekwencję. Końcowy model pokazuje, że system projektowany żąda interfejsu od Systemu 1, ale relacja jest system-system. A to powoduje, że gubimy informację podczas realizacji której usługi dla aktora system projektowany będzie potrzebował tego interfejsu. Zgodzi się Pan, że taka informacja jest bardzo ważna chociażby przy przygotowywaniu i realizowaniu testów. Taka informacja jest też przydatna przy dzieleniu pracy pomiędzy programistów.
Jeśli więc miałbym już modelować tak jak Pan, związek realizacji przypiąłbym do konkretnego przypadku użycia, a nie granicy systemu, ponieważ to czy System 1 jest aktorem czy nie zależy od konkretnej usługi, a nie systemu per se (rysunek nr 2 wyraźnie pokazuje, że 2 systemy mogą być dla siebie nawzajem i aktorami i “wykonawcami”, a wszystko zależy od tego jaki akurat rozpatrujemy przypadek użycia).
p.s.
Jeśli już Pan przywołuje nazwisko guru use case’ów, to proszę o przywołanie go poprawnie, tj. A. Cockburn. I wcale nie dziwię się, że Pan za nim nie przepada, ponieważ rozjechaliście się na poziomie definicji podstawowych pojęć. Czytając Pana wpisy dot. analizy biznesowej odnoszę wrażenie, że dla Pana celem analizy jest zbudowanie modelu, napisanie “dokumentu”, co ważne przy purystycznym użyciu notacji UML. Dla Alistair’a przypadek użycia (nie mający nic wspólnego z UMLem) jest jedynie narzędziem, jednym z wielu możliwych, stosowanym w celu rozwiązania postawionego problemu. Celem analizy biznesowej (systemowej tak, ale nie biznesowej) nie jest produkowanie kolejnych diagramów tylko dostarczenie rozwiązania o oczekiwanej wartości (korzyści).
Od końca: Cockburn – za literówkę przepraszam obu Panów. Dla mnie celem analizy jest zrozumienie “zjawiska” i zbudowanie jego modelu. Dokumentacja to tylko gadżet. Notacja UML czy BPMN to język, łamanie zasad jakiegokolwiek języka prowadzi do utraty komunikacji. Problem z AC to fakt użycia pojęcia Przypadek Użycia w zupełnie innym znaczeniu niż w UML. Nie ma to żadnego znacznie nie licząc pewnego chaosu pojęciowego jaki wprowadził, w zasadzie strzelił sobie tym w kolano. AC “modeluje” strukturę oprogramowania tabelkami, jeżeli tak nie jest to po co ta cała wielka konstrukcja opisowa.
Co do interfejsów: pakiet oprogramowania może mieć zarówno oferowane jak i żądane interfejsy, warto je rozpatrywać w osobnych kontekstach, sam fakt istnienia “bogatego API” jako “jedna sztuka” ukrywa sens poszczególnych poleceń.
Metoda naukowa, owszem jest pewną “bufonadą”, wymaga postawienia tezy, ta jest prawdziwa do momentu jej obalenia, nie trzeba jej “udowadniać” (Popper, falsyfikacja, metoda naukowa).
Bankomat: zależnie od kontekstu jest podsystemem systemu bankowego lub samodzielnym systemem. Teoria systemów zwraca uwagę na kontekst: system to system systemów, kontekst konkretnej analizy (modelowania) jest istotny.
Informacja (dane) to parametr wywołania usługi…
No i na koniec ważne :): opisuję jedno z możliwych podejść a nie jedynie słuszne …