Showing posts with label ddd. Show all posts
Showing posts with label ddd. Show all posts

Friday, October 30, 2009

Med domänen i centrum

Jag såg ett inslag om överdosering av läkemedel på Astrid Lindgrens Barnsjukhus på Aktuellt härom dagen där en barnläkare berättade om ett datorprogram som användes på sjukhuset:

- Väldigt ofta som doktor tänker jag hur många milligram som patienten ska ha. Men i journalsystemet måste jag ordinera i volym eller antal tabletter. Så jag måste först tänka vad patienten i slutändan ska ha, och sedan gå tillbaka och tänka ut hur patienten ska få det. En önskan hade varit att direkt kunna ange milligram per kilo, och att patientens vikt skulle finnas med i systemet.

Vid något tillfälle hade man tagt fel på milligram och milliliter när man doserat smärtstillande, vilket ledde till en tio gånger för hög dos. Det här kunde fått oerhört allvarliga konsekvenser.

Jag har visserligen ingen insyn i detaljerna kring utvecklingen av det här journalsystemet, men det är väl ingen vågad gissning att man inte arbetat tillsammans med någon som är expert på hur läkemedel doseras när man byggt systemet. Det här är ett smärtsamt tydligt exempel på när idéerna och principerna från domändriven design är avgörande.

DDD handlar i grund och botten om att gå tillbaka och fråga sig varför man över huvud taget bygger mjukvara. Vad ska den användas till, vilken process ska den underlätta? Vi tar vår utgångspunkt i domänen, verksamhetsområdet, och låter den genomsyra arbetet och produkten.

Som programmerare är vi förhoppningsvis experter på att utveckla mjukvara. Vi kan allt om polymorfism, hashnycklar och skillnaden mellan inner och outer join. Däremot är vi väldigt sällan experter på den verksamhet där vår mjukvara ska användas, som i fallet ovan kanske kan beskrivas som journalföring eller helt enkelt medicin.

Det finns alltså ett kunskapsglapp som vi kan tjäna väldigt mycket på om vi kan överbrygga det på ett effektivt sätt. Resultatet, menar jag, blir mjukvara som fungerar bättre och är lättare att förändra över tiden eftersom den är utformad på ett sätt som intimt hänger ihop med hur domänen fungerar.

Jag tänkte visa hur ett arbetssätt inspirerat av DDD hade kunnat undvika att hamna i den här situationen, med en fiktiv berättelse om hur utvecklingen hade gått till.

Notera att jag hittat på detaljerna i dialogen nedan utifrån vad jag läst i nyhetsartiklar, det ska inte ses som medicinska råd eller så.


I den bästa av världar


Vid något tillfälle kan man anta att man kommer fram till en user story i backloggen som ser ut såhär:
  • Som läkare vill jag kunna ange mängden läkemedel i en dos i patientens journal.
Teamet jobbar redan med en rik domänmodell som fångar upp och organiserar den affärslogik och komplexitet som är relevant för den omfattning av journalystemet man byggt så här långt, i ett väl isolerat och testbart lager av applikationen. Dessutom har man etablerat ett regelbundet samarbete med Doktor Andersson, där man lär sig om hur läkare arbetar med patienters journaler och verifierar att vissa antaganden man gjort är korrekta.

Nu berör man för första gången området dosering av läkemedel, och tar upp ämnet till diskussion med Doktor Andersson.


Engagerad Utvecklare: - I den här sprinten ska vi bygga en del funktioner för dosering av läkemedel, så vi skulle behöva veta lite om hur det går till.

Doktor Andersson: - Ja, alltså man brukar besluta om en dos utifrån patientens tillstånd förstås, och den förs in i journalen. Själva medicineringen sköts av sjuksköterskorna på avdelningen.

EU: - Hur kan en sådan...notering se ut? Säger man notering?

DA: - En ordinering, som det heter, kan vara t ex "Zeffix, IV, 20 mg/4h". Det betyder alltså läkemedlet Zeffix, intravenöst, var fjärde timme.

EU: - Ok. Hur går själva beslutsprocessen till, alltså för en läkare, när man väl har ställt diagnos?

DA: - Vi har ett system som heter FASS, eller det är väl egentligen en katalogisering av i princip alla godkända läkemedel, där man kan söka och bläddra bland läkemedel utifrån vad man behöver behandla och så vidare. Det finns kategorier och underkategorier med ATC-koder. Sånt brukar ni systemvetare vara intresserade av.

EU: -Vi är inte systemv...hrm, ok. Har varje läkemedel en egen, unik ATC-kod?

DA: - Nej, det kan finnas några likartade alternativ för en viss ATC-kod. Ibland finns det bara ett.

EU: - Vad står i FASS om varje läkemedel som är relevant för ordinering då?

DA: - Väldigt mycket. Användningsområde, biverkningar, olika typer av riskfaktorer och rekommenderad dos.

EU: - Hmm...i det här exemplet med Zeffix står det 20 mg, men hur vet man att det ska vara just så mycket? FASS borde väl rimligtvis säga något om koncentrationen, eller?

DA: - Visst, så är det. Man ordinerar alltid verksam substans i milligram per kilo kroppsvikt, så den dosen är individuell och beror på patientens vikt. Dessutom varierar det mellan barn och vuxna, och ibland för gravida och liknande. En sak som skulle vara till otroligt stor hjälp är om man kunde ha någon sorts automatisk omvandling från milligram till det sätt som läkemedlets mängd anges, alltså i milliliter för flytande form, antal tabletter om det kommer i tablettform och så vidare.

EU: - Mm, jag förstår. Kan man säga något om maximal dos? Minimal? Kan man över huvud taget underdosera?

DA: - Jodå, man kan underdosera. Det för en rad problem med sig, som vi kanske inte behöver gå igenom i detalj, men det är i alla fall inte bra. FASS säger inget om maximal eller minimal dos, det är upp till läkaren att besluta.

EU: - Hur mycket brukar det variera? Hur ofta ger man mer än dubbla rekommenderade dosen till exempel? Vore det önskvärt om systemet kunde avgöra om dosen är osedvanligt hög eller låg, och varna eller blockera? Hur skulle man kunna avgöra det i så fall?

DA: - Hmm...det är inte alls ovanligt att man doserar annorlunda än rekommendationen, i viss utsträckning, men det beror förstås på läkemedlet och en del annat. Det vore bra om man åtminstone kunde se hur dosen förhåller sig till rekommendationen, i procent eller liknande.

Så här fortsätter samtalet. Under tiden har man skissat ihop det här på whiteboarden:



Efter mötet med domänexperten sätter sig två personer i teamet ner och parkodar fram ett utkast på hur ordinering av Zeffix till en patient kan se ut, med utgångspunkt i vad man nyligen har lärt sig och det språk som använts under samtalen.


@Test
public void ordinationEnligtRekommendation() {
Läkemedel läkemedel = FASS.slåUpp("Zeffix");

Personnummer personnummer = new Personnummer(new LocalDate(1950, 1, 2), 1234);
Amount<Mass> kroppsvikt = valueOf(75, KILOGRAM);
Patient patient = new Patient(personnummer, kroppsvikt);

Koncentration koncentration = Koncentration.mgPerKgKroppsvikt(20);
Frekvens frekvens = Frekvens.ggrPerDygn(4);
Ordination ordination = new Ordination(läkemedel, koncentration, frekvens);

Dos dos = patient.dosVidOrdination(ordination);

assertThat(dos.läkemedel(), is(läkemedel));

Amount<Volume> förväntadMängd = valueOf(300, 0.001, MILLI(LITER));
assertTrue(dos.mängdPerTillfälle().approximates(förväntadMängd));

assertThat(dos.relativtRekommendation(), is(1.0));
assertFalse(dos.överdosering());
assertFalse(dos.underdosering());
}


(Den kompletta (körbara) koden finns på Github för den som är intresserad)

Här kan man tydligt se hur språk, modell och kod hör ihop på ett intimt sätt, och man har fokuserat på att verkligen lösa ett problem i domänen på ett sätt som stämmer överens med domänexpertens uppfattning av hur det fungerar.

Teamet kan nu ta med sig funderingar och frågor som dykt upp under programmeringsfasen in till nästa modelleringsmöte, som till exempel hur man hanterar läkemedel som kommer i tabellform, något som den första versionen av modellen inte klarar av.


Vad av det här är DDD?

Domändriven design är inget ramverk och ingen process, utan brukar lite luddigt beskrivas som ett förhållningssätt till att utveckla mjukvara, en uppsättning principer, en filosofi. Det är många små beståndsdelar i samverkan, och i exemplet ovan har jag försökt illustrera några av de viktigaste. Här sammanfattar jag dem i punktform:
  1. Sätt domänen i centrum och prata med en domänexpert

    Leta reda på en person som har lång erfarenhet av domänen, som vet hur verksamheten fungerar och varför. Försöka utvinna så mycket kunskap som möjligt från den här personen för att kunna fatta bättre beslut vid designen av koden.

  2. Utforma ett gemensamt språk

    Identifiera viktiga termer och begrepp i domänexpertens sätt att uttrycka sig. Säkerställ att ni är överens om betydelsen. Inför nya begrepp om det behövs.

  3. Bygg en modell

    Ställ upp tänkbara scenarier. Peka på rutorna på whiteboarden och förklara högt hur de ingående delarna kan kombineras för att lösa problemet. Om nåt känns avigt, tänk om, förändra modellen. Experimentera.

  4. Representera modellen i kod

    En modell som inte fungerar när man implementerar den i kod är i princip värdelös, så börja programmera så fort som möjligt. Jobba testdrivet genom att ställa upp scenarier med hjälp av testklasser. Använd det gemensamma språket för att döpa klasser, metoder och paket. Sträva efter att få koden att berätta vad den håller på med på ett sätt som en domänexpert kan förstå.

  5. Använd byggstenarna

    DDD handlar till stor del om att utnyttja kraften i objektorientering, och lyfter fram ett antal designmönster som stöd för att organisera koden i modellen och hålla nere komplexiteten. Studera och använd byggstenar som entity, value object, repository med flera.

  6. Fokusera på det viktiga

    Fundera på vad som verkligen är viktigt och unikt för den här produkten, och fokusera på det. För alla områden som är relaterade till men inte unika för den här domänen, utnyttja någon annans arbete. Saker som tid- och datumhantering eller manipulation av enheter och stoheter, massa och volym har andra med stor sannolikhet redan stött på och byggt verktyg för.

  7. Återkoppla erfarenheter från koden

    När man omsätter kunskap och modell i konkret kod dyker nya utmaningar och frågor upp. Använd dem för att förändra och förbättra modellen, och ta med dem till nästa möte med domänexperten för att få stöd att fatta bättre designbeslut.


Reklam: Citerus gillar DDD och vi vill gärna dela med oss av vår kunskap. Kika in på citerus.se/ddd för mer information.

Friday, March 27, 2009

DDDSample 1.1.0 released

What the title says :-)

It's been 6 months since 1.0, and quite a lot has happened. If you're interested in domain-driven design, take a look at it and let us know what you think, either on the international Yahoo DDD group or the Swedish Google group.

If you're interested in learning more about DDD, Citerus has a variety of offers for you and your team.

Sunday, November 23, 2008

On layering in DDDSample

There has been considerable interest in the DDDSample application on the Swedish DDD user group mailing list - people are scrutinizing the code, asking questions and raising concerns. This last week has been mostly about layers and packages, and I think this blog is a good forum to provide a little background and explain the rationale behind how the sample application is structured.

When we first started working on the application, we used a fairly standard layering with a web user interface layer, a service layer with interfaces, implementations, transaction demarcation and so on, and a repository layer for persistence, implemented in Hibernate (roughly matching the DAO layer found in most applications). These layers all resided in their own package hirearchy. In addition to that, the domain model had its own package, although it wasn't a layer in the usual sense - it was used by several other layers, but calls did not pass through the domain model on to some other layer. This suited us well for the time being, since there were many other aspects of the application that required our immediate attention.

As the application grew more mature, we began looking at how the structure of the application could illustrate important DDD concepts such as aggregates and isolating the domain. In his book, Eric Evans uses this diagram when talking about layered archicture:


By far the most important layer is the domain layer, so we decided to take a closer look at the contents of our domain layer and the domain package. A pretty obvious decision was to place each aggregate in its own subpackage below domain, so we had domain.cargo, domain.handling and so on. Deciding which services were domain services and which were application services was harder, but we settled for a separation where domain services performed tasks that you could talk about with a domain expert, using the ubiquitous language. Signatures consisted completely of domain model types. In some cases, it was natural to place the interface of a domain service in the domain layer, but the implementation elsewhere.

But the decision that would turn out to be the most controversial was placing the repository definitions (i.e. the interfaces) in the domain layer, alongside the aggregate root for which it was used to retrieve, store and search. The concept of an aggregate root is closely linked to that of a repository: all access to an aggregate is through the root, so consequently the repository works with aggregate roots, and there is one repository per aggregate root (and thus per aggregate). Also, repositories are expressed in the ubiquitous language.

At this point our domain layer consisted of the domain model, separated into aggregates, domain services and one repository per aggregate, which we felt pretty good about (and still do). It expressed many important DDD concepts in a clear way, and it was slightly unorthodox compared to many mainstream designs. All this was located under the domain package.

The question of what to do with the rest of the application remained. The other three layers are not as interesting from a DDD perspective, so we decided not to pursue the effort of organizing the rest of the code into per-layer-package hierarchies, but instead separate it from the domain package and organize it by a combination of technology and use cases.

We did however consciously include two very different approaches to user interface exposure. The tracking web interface, which runs in the same JVM as the main application, is the low-overhead-thight-coupling way of doing it (in terms of layers and lines of code), where the MVC controller acts as application layer and calls the domain layer repository directly. The tracked cargo is thinly wrapped in the view rendering phase to make it easier to work with in a JSP EL environment.

The booking web interface on the other hand can run in a different JVM and works against an RMI facade on top of the domain layer, passing custom DTOs back and forth. The point is that we don't generally recommend a mandatory, slavishly delegating application layer between the presentation and the domain layers. Sometimes the controller in the MVC layer can play the part of application just as well. Another important point here is that you should always shield the domain model objects from presentation requirements, and DTOs or a thin presentation wrapper and so on are good options for doing that.

The top level package for all non-domain-layer code was named application, which turned out to be a bad idea (mine) since the name coincided with one the the layers in the picture above. For the record, the rationale behind it was "application" as in "computer program", i.e. everything about the code that wasn't part of the domain. A better name would have been something like nondomain or even other.

This problem immediately became apparent when I presented the application to the New York DDD User Group, so a separate ui package was extracted for the web MVC controllers and supporting code. This actually turned out to make matters slightly worse, since we now had top-level packages with names matching three out of the four layers in the DDD layer model, immediatly leading people to ask for the missing infrastructure package. The discussions on the mailing list and other forums helped us realize that there is a need for an explicit infrastructure package, so the next version of DDDSample will include such a package containing the parts that we consider to be part of the infrastructure layer.

It appears from the discussions on the Swedish user group mailing list that many people think of the infrastructure as being identical to the persistence aspect (the database and the O/R mapper), but we have a wider definition of infrastructure which also includes messaging, scheduling, thread pools, the Spring container, the servlet container and external services such as mail senders and in our case the routing team's graph path finding service.

Looking at the picture above, the arrows between the layers actually illustrate the point quite well: the presentation, service and domain layers all work with the infrastructure layer, but it's important to realize that it doesn't mean that you should execute SQL statements in your JSP pages, but rather that each layer interacts with some part of the infrastructure. Also, the infrastructure layer can be used for passing asynchronous messages between layers.

In general, we consider code and configuration files that we write in order to hook into the infrastructure to be part of the infrastructure layer - Hibernate repository implementations, HBM mapping files, Spring context definition files, the RoutingService implementation and so on. Sometimes the distinction is harder to make, such as having an application service implement the JMS MessageListener interface to act message-driven.

As a rule of thumb, never state rules of thumb when it comes to software development. But if I were to do that anyway, I'd say that the infrastructure layer should be completely separable from the rest of the application by stubbing out external services, implementing persistence in-memory, and using synchronous calls or simple threads for messaging.