Anemic domain model
Heinäkuu 6th, 2007
Tuli tässä taas keskusteltua Martin Fowlerin ristiriitaisen vastaanoton saaneesta “anti-patternista”, nimittäin anemic domain modelista.
Osa ohjelmoijista nimittäin on sitä mieltä, että domain-luokkiin voi huoletta lisätä myös logiikkaa. Itse olen aina ollut vahvasti sillä kannalla, että logiikkaa ja dataa sisältävät luokat tulee pitää tiukasti erillään.
Otetaanpa esimerkki eräältä “anemic domain”-mallia vastustavalta sivustolta:
HUOM: Tämä EI siis ole meikäläisen kirjoittamaa koodia!
@javax.persistence.Entity
public class Acount {
@javax.persistence.Transient
private EmailService email;
@javax.persistence.Id
private Long id;
private float balance;
public void withdraw(float amount) {
if (amount>balance) {
email.sendOverdrawMessage(this);
}
balance-=amount;
}
...
Todella karseaa koodia. Ensinnäkin pahaa tekee floatin käyttö rahan käsittelyssä ja toisekseen servicen sijoittaminen domain-luokkaan. No, muutenkin esimerkki on aika kaukana reaalimaailmasta.. Service-kutsujen viljely domain-luokkien sekaan tekee logiikan seuraamisesta erittäin hankalaa ja aiheuttaa nähdäkseni syklisiä riippuvuuksia sekä mahdollisia muistivuotoja.
Otetaan yksinkertaisempi esimerkki:
Perinteinen, “anemic”-malli:
printer.print(document);
Ja anemic-mallin vastakohta (mikähän sen nimi sitten onkaan):
document.print();
Tässä tosiaan toteutetaan ajatusta “Don’t ask, tell”. Ongelmaksi vain muodostuu se, että ominaisuuksien lisääntyessä, domain-luokasta tulee jatkuvasti bloatimpi. Jos esimerkiksi lisätään dokumenttiin tallennusominaisuus, sähköpostin lähetysominaisuus, PDF-konversio-ominaisuus, tulee niistä kaikista uusi riippuvuus dokumenttiolion sisään. Miksi ihmeessä dokumentin pitäisi osata esimerkiksi tulostaa itsensä? Kuuluuko se dokumentin tehtäviin? Tämä sotii mielestäni separation of concerns-ajatusta vastaan.
Lisäksi joissain tapauksissa ongelmaksi muodostuu olion serialisointi verkon yli. Yht’äkkiä hajautetussa transaktiossa toimivat palvelut saavatkin domain-luokan mukana tukun riippuvuuksia epämääräiseen joukkoon palveluita..
Lisäksi tuossa jälkimmäisessä tosiaan tulee syklinen riippuvuus domain luokkien ja service-kerroksen kanssa:
client -> DocumentService.process(List documents) -> document.print() -> PrintService.print(document)
Perinteisessä mallissa domain-luokat taas eivät ole riippuvaisia service-kerroksesta:
client -> DocumentService.process(List documents) -> PrintService.print(document)
Itse kutsuisin Fowlerin aneemiseksi tituleeraamaa mallia vaikkapa “light-weight domain modeliksi”. Yksinkertaisuus kun ei mielestäni ole pahe koodissa.
No, paljon jäi sanomatta, mutta tällaisia mietteitä asiasta noin äkkiseltään tuli. Olisi ihan mielenkiintoista kuulla, että onko jollain menestystarinoita logiikan upottamisesta domain-malliin? Ja vanhoja entity beaneja en laske menestystarinaksi.
Artikkeli on luettu 1052 kertaa. Kuuluu luokkiin: Ajatuksia, Ohjelmointi, Java
3 Kommenttia Lisää kommentti
1. Artti Jaakkola | Heinäkuu 7th, 2007 at 2.02
Nähtävästi ensimmäinen esimerkki onkin kaikkien mielestä karsea.
Lueskelin lisää aiheesta ja useammalla “rich domain modelia” ajavalla saitilla suositellaan domain-malliin sijoitettavaksi liiketoimintasääntöjä, laskentasääntöjä ja validointeja. Nämähän eivät useasti ole suoraan riippuvaisia palvelukerroksesta:
En ole oikein tästäkään vakuuttunut. Joissain tapauksissa esimerkiksi liiketoiminta- tai validointisäännöt ovat staattisia, eivätkä muutu tilanteen mukaan (esimerkiksi shakkipelissä säännöt ovat aina samat), mutta esimerkiksi liiketoimintajärjestelmissä sääntöjä tulee voida muuttaa, eikä niitä näin ollen voi kovakoodata domain-luokkiin.
Tietysti domainlogiikkaa (kuten Fowler sitä kutsuu) voidaan parametroida varsin vapaasti, mutta siltikin sijoittaisin nämäkin toiminnallisuudet domainin ulkopuolelle.
Kuten asia JBoss Rulesin dokumentaatiossa ilmaistaan:
“- Logic and Data Separation
Your data is in your domain objects, the logic is in the rules. This is fundamentally breaking the OO coupling of data and logic (this can be an advantage as well as a disadvantage depending on your point of view). The upshot is that the logic can be much easier to maintain as there are changes in the future, as the logic is all layed out in rules. ”
2. mika | Helmikuu 29th, 2008 at 11.29
terve, katos mitä löyty kun googlasi aneemista
tämmönen datan ja logiikan erotteluhan on ihan kamalaa, ei se ole mikään fowlerin kontroversiaali idea vaan ikiaikainen OO-periaate. domainluokissa _pitää_ olla logiikkaa ! muuten kannattaa harkita jotain möhkö-service-haroo-JDBC:tä-tekniikkaa ilman minkäänsorttista mallia.
jos domain-luokka lihoo niin silloin kannattaa miettiä onko se domainmalli tarpeeksi hienojakoinen. jonnekin se bloat joka tapauksessa menee, terv. nimim. “olen nähnyt 7000 rivin service-luokkia”.
samaa mieltä kyllä service-kutsuista domain modelin seassa, ihan liian sekavaa kenen pitäisi kutsua missä ja ketä, onko joku kutsunut jne. yksi tapa on pitää ne poissa domain-luokista ja tehdä kutsut service-tasolla. nyt siis service == “ulkoinen” järjestelmä niinkuin tietokanta tai “documentService”.
serialisoinnista … sitä voisi ensinnäkin välttää kun vain mahdollista ja jos on pakko kuljetella niitä jonnekin niin silloin se on DTO-aika …
3. Artti Jaakkola | Maaliskuu 12th, 2008 at 12.30
Moikka
> ei se ole mikään fowlerin kontroversiaali idea
> vaan ikiaikainen OO-periaate. domainluokissa
> _pitää_ olla logiikkaa !
Miksi? Mitä sillä saavutetaan ja miten se parantaa koodin ylläpidettävyyttä?
Minulle ei riitä se, että ukko ylijumala Fowler sanoo jossain rapakon takana, että tehkää näin. Se, että joku hämäräveikko kirjoittaa 7000 rivisen service-luokan, ei mielestäni riitä myöskään perusteluksi sille, että sovelluslogiikkaa vietäisiin domain-malliin. Juuri tämän takia ns. service-domain-jaottelusta on nähdäkseni tullut niin suosittu.
Serialisoinnista pitäisikin sitten kirjoittaa oma juttunsa.
Jätä kommentti
Sallitut HTML-elementit:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed