Anemic domain model
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.
3 kommenttia Heinäkuu 6th, 2007