29 paź 2008

MDD

No więc to nie będzie o UMLu, ani o MDA ani o MDSD ani niczym podobnym. Tylko o projektowaniu aplikacji OO.
- Jak Ty zaczynasz swoją aplikację?
- Hmm, no więc... zwykle to... jakoś tak wychodzi...
- No, no! Przyznaj się! Od UI czy od bazy danych?

No więc? Od czego?
Jakoś tak wychodzi, że zastraszająco dużo projektów zaczyna się od tzw. d* strony. Czyli właśnie albo od UI albo od DB. Generalnie od jakiejś infrastruktury. Od frameworków. Od bibliotek. I myślę, że jednym z powodów jest brak jasnej wizji aplikacji na początku jej tworzenia. A jak wykorzystać frameworki wiadomo. Jak ma wyglądać UI też zwykle (przynajmniej z grubsza) wiadomo. A potem się gdzieś te funkcjonalności wsadzi. A żeby móc zacząć pisać funkcjonalności trzeba mieć konkretną wizję systemu, którą można mieć wyłącznie poprzez wcześniejsze jego zamodelowanie.

Model-Driven Design
Model-Driven Design oznacza, że development aplikacji prowadzimy (drive'ujemy :) ) modelem.
W książce o DDD Eric Evans wyróżnił 8 głównych wzorców budowy aplikacji. I tak naprawdę nic nowego pewnie tu nie ma poza tym, że pewnie powinni tego uczyć na każdym kursie OO na uczelniach. A że chyba nikt tego nie robi, więc są tu :)
  • Layered Architecture
  • Entities
  • Value Objects
  • Services
  • Modules
  • Aggregates
  • Factories
  • Repositories
Zachowałem oryginalne angielskie nazewnictwo, bo tak naprawdę są to wzorce projektowe a częścią idei wzorców jest uwspólnienie języka.

Layered Architecture
Jeśli warstwowość rozumiemy jako oddzielenie zagadnień (separation of concerns) czyli np. nie wrzucanie do jednej klasy kodu UI, DB i jeszcze funkcjonalności aplikacji, to jest to oczywiście rzecz wielce porządana. Zwykle wyróżnia się: UI, aplikacja (koordynacja aplikacji, bez logiki biznesowej), dziedzina (obiekty dziedziny wraz z logiką i stanem), infrastruktura (komunikacja między warstwami, utrwalanie obiektów, dodatkowe funkcje usługowe dla aplikacji, etc.)

Entities
To te elementy dziedziny, które mają określony identyfikator. I to nie atrybuty tych obiektów decydują o tym czym są, tylko właśnie identyfikator. Typowe przykłady to Klient, Firma, Faktura itp.

Value Objects
To przeciwieństwo entities. Tu wyróżnikiem jest zawartość obiektu, jego atrybuty, a nie identyfikator. Przykłady to np. Pieniądz (ważna zwykle wartość a nie nr seryjny) czy Książka (ważny jest autor i tytuł a nie konkretny egzemplarz).

Services
Czasem się okazuje, że są takie elementy aplikacji które kiepsko pasują do idei modelowania i dziedziny, a muszą być zaimplementowane. Czasem jakaś funkcjonalność nie pasuje do żadnego z wyróżnionych obiektów a jednak ich dotyczy. Albo dotyczy wielu różnych obiektów i modelowanie jej za pomocą dziedziczenia psuło by tylko model. Pisałem kiedyś aplikację w której musieliśmy zwracać w postaci RSS różne zupełnie obiekty (nie miały ze sobą nic wspólnego) i dodawanie funkcjonalności eksportu w postaci RSS do każdego z nich nie miało sensu. I wtedy taka usługa abstrahująca od typu obiektu była idealna (Tak trochę off-topic to są języki w których można to zrobić ładnie i w ramach dziedziny. Np. w Scali można zrobić trait i zaaplikować do wszystkich obiektów o które nam chodzi. W aspectj, groovym i rubym można zrobić mixin. Więc co właściwie pasuje do tej kategorii zależy trochę od języka z którego korzystasz.)

Modules
To normalna praktyka programistyczna. Dzielenie aplikacji (w tym przypadku chodzi głównie o model dziedziny) na moduły o niskim poziomie zależności pomiędzy nimi, ale o wysokiej wewnętrznej spójności (zarówno z punktu widzenia komunikacji jak i funkcjonalności).

Aggregates
To znowu wzorzec strukturalny. Tym razem chodzi o zebranie w jeden agregat tych obiektów dziedziny które mają wspólny cykl życia (tworzenie, usuwanie). To powoduje zmniejszenie liczby relacji między obiektami dziedziny (zamiast odnosić się do poszczególnych drobnoziarnistych obiektów, dotyczą całego agregatu). Dobry agregat ma dobrze określone granice dostępu do niego, tzn. jest w nim jeden główny obiekt (korzeń) i tylko przez niego (przez jego relacje) możemy dostać się do obiektów zależnych. Zewnętrzne obiekty nie mogą mieć odniesień do żadnych elementów wewnętrznych agregatu poza tym jednym - korzeniem.

Factories
Biorąc pod uwagę, że tworzenie encji, a tym bardziej agregatów, może być dość złożone dla bardziej skomplikowanych obiektów, warto wykorzystać fabrykę (a'la GoF) .

Repositories
Jednym z podstawowych założeń dobrego projektu jest minimalna zależność dziedziny od reszty aplikacji. W szczególności dotyczy to kontaktu z UI i infrastrukturą (np. DB). Dlatego dobry rozwiązaniem jest obiekt, który realizuje CRUD na obiektach dziedziny. Dzięki temu nie muszą one same dostawać się do innych (przez bezpośredni dostęp do bazy ani DAO) lecz mają specyfikowany wygodny interfejs, który w dodatku może realizować dodatkowe funkcjonalności takie jak caching czy rozwiązywanie zależności.

Oczywiście najważniejszym elementem w MDD jest modelowanie dziedziny. I to nie raz, na początku projektu. To ciągła weryfikacja i modyfikacja poprawności, logiczności, sensowności struktury aplikacji. Wraz z jej rozwojem i Twoim rozumieniem dziedziny. Tutaj kłania się oczywiście TDD, ale to inna bajka.
A jak to się ma do tego jak robisz swoje aplikacje?

20 paź 2008

Wszechobecny język

2 lata temu powstała książka 'Domain Driven Design' Erica Evansa. Kto nie czytał - polecam. Książkę można podsumować jednym zdaniem: Object-Oriented design done right. Treść nie powinna zaskoczyć żadnego programisty OO, ale i tak warto przeczytać, ponieważ dość zgrabnie podsumowuje podstawowe zasady projektowania aplikacji obiektowych. Składa się ona z trzech podstawowych części:
- Wszechobecny Język (Ubiquitous Language)
- Model-Driven Design
- Zachowywanie spójności modelu

Zamieszczam tu telegraficzny skrót tej książki, bo uważam ją za ważny element edukacji programistycznej.

Tak więc w tym poście podstawowy element DDD:

Wszechobecny Język to idea jednego wspólnego języka dla dziedziny (tak jak rozumie ją użytkownik/klient/biznes), aplikacji i języka komunikacji programistów. Brzmi dość logicznie - tych samych nazw używamy w rozmowie z biznesem (klientem, analitykami, ekspertami, itp. itd...), w kodzie i w zespole - to ułatwia komunikację (najwięcej problemów w projektach leży właśnie tu) i zmniejsza ryzyko nieporozumień.
Wszystko świetnie brzmi jak się ma klienta anglojęzycznego. W większości projektów mamy jednak Polskojęzycznego klienta. Polskie (czy generalnie nieangielskie) nazwy w kodzie (zmienne, metody, komentarze) uważam za zły pomysł ze względu na 2 rzeczy - globalność naszego zawodu (dziś kod piszesz ty, jutro firma w Indiach albo Chinach) oraz czytelność (findFirmaByUlica czyta się strasznie) . Jestem więc zwolennikiem tłumaczeń na angielski, choć czasami nie jest to możliwe (NIP, Pesel itp. są kiepsko tłumaczalne).
Wszechobecny język to również idea sposobu komunikacji z biznesem - używanie ich języka to nie wszystko. Trzeba by dodać jeszcze nieużywanie naszego. Jak ekspert od ubezpieczeń mów:
"Chciałbym, żeby się dało usuwać polisy."
a ty zapytasz go:
"A wystarczy jak usunę rekord z tabelki? Czy mam dorzucać nowy z markerem, że usunięte?"
To boję się, że jest szansa na poziomie 50%, że on nie wie o czym mówisz, a 90% że nie wie po co ten marker i jakie są tego następstwa takiej decyzji. Nie mówiąc o tym, że decydowanie na tym etapie o implementacji jest pewnie przedwczesne. Powinieneś więc pewnie raczej zapytać np.:
"A czy potrzebujecie zachowywać kompletną historię dla każdej polisy?"
"A co z ochroną danych osobowych?"
Tak więc to właśnie od programisty/projektanta oczekuje się mówienia językiem biznesu i projektowanie w tych właśnie terminach a nie od analityka / eksperta znajomości nomenklatury i idei technologicznych. Dzięki temu dobrze napisany kod (nawet w Javie) może być czytelny prawie jak specyfikacja funkcjonalności i jakby się uprzeć, to można go pokazać ekspertowi i zapytać czy o to chodzi.
Poniżej kod z jednego z projektów które pisałem.
class Contract {
  ...
    public void suspendThisAndRelatedContracts () {
        if (alreadySuspended())
            return;
        makeSuspended();
        Collection contracts = findRelatedContracts();
        suspendGivenConracts(contracts);
        executeSuspensionWorkflow();
    }
  ...
    private void suspendGivenConracts(Collection contracts) {
        for (Contract c : contracts)
            c.suspendThisAndRelatedContracts();
    }
  ...
}
Nawet bez znajomości dziedziny można dokładnie powiedzieć co się dzieje. Co więcej taki kod można dość łatwo wytłumaczyć i przedyskutować jego poprawność w sensie funkcjonalności z ekspertem/klientem.

17 paź 2008

OO

Dobrze pamiętam 2 rok studiów. Przedmiot: Programowanie Obiektowe. Język: C++. Projekt na zaliczenie: Komisariat Policji. Trzeba było zrobić 5 klas na krzyż wykorzystać słowa kluczowe virtual, friend, protected i miało się ocenę. Normalnie ekstra.
Ale jednak teoria była dość jasna. Książka do tego pewnie jakaś była (nawet nie pamiętam czy była...) i generalnie człowiek uwierzył, że tak się pisze oprogramowanie (choć przez wcześniejsze dwa semestry wmawiali co innego...)

A potem poszedłem do pracy. A tam servlety (tak, tak, wtedy pisało się servlety...), JSP, Struts i z dnia na dzień okazało się, że jednak nie. Że kod realizujący funkcje oczekiwane przez klienta pisze się we frameworku. To daje ogromne możliwości. Po pierwsze szybko się pisze - wystarczy podziedziczyć (mówiłem, że OO) po klasie Action. Po drugie framework daje Ci wszystko czego możesz potrzebować. Tak czy siak cała moja nauka o OO na którą pracował stolarz w Radomiu, piekarz w Ciechanowie i pucybut w Galerii Mokotów (no i pewnie jeszcze paru innych podatników) poszła na marne.

Ale do rzeczy. Bo może jednak nie poszła. To będzie takie mini przypomnienie absolutnych podstaw OO. Przeczytaj i zastanów się jak aplikacja, nad którą aktualnie pracujesz ma się do tego.

Kod aplikacji powinien być w obiektach. Nie w akcjach. Nie w komendach. Nie w managerach. Ani nawet nie w Session Beanach EJB3. Tylko w obiektach. Dokładniej to w klasach, bo java realizuje obiekty wyłącznie poprzez instancje klas, więc powiedzmy, że w klasach. (Martin Fowler nazwał je POJO w odróżnieniu właśnie od klas związanych ze wszelkimi wzorcami, frameworkami, platformami itp.)

Poza skrajnymi przypadkami każda aplikacja powinna być rozpoczynana od modelowania (tak, to jest agile - przypomnę, że słówko "agile" nie oznacza "bezmyślnie", tylko "sprawnie" - sprawniej jest pomyśleć i potem zrobić niż zrobić i sprawdzić czy a nuż działa). Czyli wymyślenia obiektów które będą realizowały funkcje aplikacji poprzez wzajemne relacje i przekazywanie sobie komunikatów. Czyli zanim zaczniesz pisać kod musisz wiedzieć jak wygląda twój świat. Jakie są w nim elementy. Jak mogą się komunikować. Nie do najdrobniejszych szczegółów. Ale tak, żebyś wiedział co piszesz i dlaczego.
A teraz spójrz na swoją aplikację i sprawdź jaki procent jej wymagań funkcjonalnych jest zrealizowanych wewnątrz klas reprezentujących dziedzinę twojej aplikacji.

To jest nie tylko kwestia praktyk czy podejścia. To kwestia profesjonalizmu. Odpowiedzialności. Nawet etyki zawodowej.
Jeżeli aplikacja ma żyć dłużej niż rok to jakiekolwiek wiązanie funkcjonalności aplikacji z jakimś frameworkiem/platformą/biblioteką jest marnowaniem pieniędzy klienta.
Ale jeśli nawet nie wiążemy się z żadnym frameworkiem (tajna broń EJB3) ale aplikacja nie ma przejrzystych obiektów dziedziny a zamiast tego funkcjonalności ukryte w komendach/managerach/fasadach/akcjach itp. To i tak ryzykujemy pieniędzmi i czasem klienta (czas wdrożenia nowych programistów będzie znacząco większy). Czy ktoś daje nam do tego prawo?

8 paź 2008

Sprawny Inżynier?

W sierpniu na konferencji Agile 2008 Robert C. Martin (aka Uncle Bob) zaproponował dodanie do Manifestu Agile jeszcze jednego elementu:
Craftsmanship over Crap
Nasza dziedzina (przynajmniej światek javowy, który oglądam na co dzień) pogrążona jest w odmętach totalnego braku profesjonalizmu. Mamy masę nowoczesnych technologii (bibliotek, frameworków), masę mądrych ludzi dookoła, setki publikacji, jesteśmy bodajże najnowocześniejszą z wszystkich grup zawodowych, a przy tym jesteśmy całkowicie nieprofesjonalni.

Studenci medycyny słyszą od pierwszego roku, że ich błąd może kosztować kogoś życie. Co więcej za błędy mogą znaleźć się w więzieniu. Podobnie z prawnikami - za błędy mogą odpowiadać przed sądem i płacić za nie własnym majątkiem. A czy ktoś słyszał o programiście skazanym za:
try {
....
} catch (SQLException e) {
// maaaan! this can neeeever happen!
}
Audytowałem kiedyś aplikację e-bankingową. Paręnaście tysięcy klas. Prawie wszystkie dziedziczyły po
org.apache.struts.action.Action

Proponuję 3 lata więzienia.

Innym razem przepisywałem system przetwarzający SMSy. W oryginalnym kodzie roiło się od takich linii:
Thread.sleep(375);
Potem autor tłumaczył, że sprawdził, że jak te wątki czekają odpowiednią liczbę ms to aplikacja działa...
Rok w celi.

Znowu: klasa 1500 linii kodu. Maszyna stanowa (ponad 20 stanów) zrealizowana za pomocą jednego switch.
3 miesiące.
(Oj, nie! To mój kod akurat był...)

Właśnie - dlaczego na uczelniach uczą nas raytraycingu, drzew patricia, czy algorytmów mrówkowych a nikt nie robi ćwiczeń z projektowania kodu. Dlaczego nikt nie powiedział, że kod lepiej się pisze i jest się go pewniejszym (i jeszcze na dodatek jest utrzymywalny!) jak zacznie się od testów? (ktoś w ogóle słyszał słowo test na uczelni?) Czemu napalonym na kodowanie 20-latkom nie mówią, że nie jest fajny konkurs na najmniej czytelny kod w C/Perlu?


Agile Software Engineering - Sprawna Inżynieria Oprogramowania, czy Inżynieria Sprawnego Oprogramowania?
sprawny (za SJP PWN)
1. «dobrze wyćwiczony fizycznie, zręczny w ruchach, w wykonywaniu czegoś; też: będący objawem, dowodem czyjejś zręczności»
2. «świadczący o dobrym opanowaniu przez kogoś jakiejś umiejętności»
3. «właściwie urządzony, zorganizowany»
4. «o urządzeniu: dobrze działający, funkcjonujący»
Czy odnosi się to do mnie i do mojego kodu?

Jeżeli inżynieria, to najpierw rzemiosło! Tak więc proponuję zacząć od terminowania. Najpierw zostań czeladnikiem, na sztukę nadejdzie czas (może...) Wolałbym być operowany przez sprawnego chirurga, niż chirurga artystę :)

Taki jest plan na ten blog - krzewienie wiedzy (również własnej) w zakresie inżynierii oprogramowania. Nie chcę nigdy wstydzić się swojego kodu.