Saturday, May 31, 2008

Domain Services in Domain Driven Design (DDD)

This post is part of a series I am writing on Domain Driven Design (DDD).

"A SERVICE is an operation offered as an interface that stands alone in the model, without encapsulating state, as ENTITIES and VALUE OBJECTS do." [Evans 2003]

The above defines what a Domain Service is. Any behaviour, operation or activity that cannot logically fit into an Object is a good candidate for being exposed as a service. However the important thing to note here is that the Service being exposed should represent a domain concept. This ensures you don't get carried away and create tons of services with very less entities and vo's.

Rules to be followed by Domain Services

A domain service has to abide by only 2 cardinal principles

  1. It has to be stateless
  2. It should have some meaning in domain e.g. AccountTransferService, AdjustOrderService etc

Identifying Domain Services

I carve out a service in any one of the following conditions
  1. The concept behind an entity/vo does not lend itself to the activity being modelled. If by adding the activity concerned the meaning of the entity/vo is diluted, the activity is better off exposed as a service. In other words, if there is no obvious owner expose behaviour as a service.

  2. When the activity being modelled spans across different entities/vo's that are not part of same aggregate, expose a domain service that co-ordinates the activity across the many objects involved.

    For e.g. when adjusting or modifying an existing order, the order, payment and delivery entities have to be updated. I find it more easier and intuitive to model this as a service rather than have all logic in Order.adjust method. To access and modify Delivery/Payment entities, Order entity may have to do a deep object graph traversal. The order entity may not always be qualified to handle this traversal. The OrderAdjustService however, would use a repository to look up all concerned entities and co-ordinate the adjust method among them. This ensures all domain logic of how adjusts happen is still contained within the entities and the service just acts as an orchestrator.

    In fact I like to have an Order.adjust method that the UI/Application layer calls. This Order.adjust method would then call the OrderAdjustService passing in required info. This provides a clean interface and separation of concerns.

  3. If a certain domain logic is applicable across multiple entities and you have to work with a single inheritance model (using java for example), then the logic can be exposed as a service.

    For e.g. multiple entities like Order, Shipment etc need to expose MarketPrice behaviour. The logic to create a price includes looking up real time market prices and currency conversion rates and then calculating the price. A MarketPrice service can encapsulates this logic. The entities should be able to refer the MarketPrice service and delegate all MarketPrice related calls to that service.

    I use this when a ValueObject cannot suffice. In this case there were no fields specific to market price in entities and these were runtime generated and consumed values.

    There is a good description of this technique here. However this is not commonly used.

FAQs on Services

  1. Are'nt Services bad and should'nt we use all objects as per OO ?

    Yes, Services tend to stand orthogonal to Object Oriented Design. Services are not always bad since its far worse to force fit a behaviour into some entity/vo just to be more OO compliant. It messes up the class by distorting its conceptual clarity and makes it harder to understand.

    There is a huge tendency in the modelling world to use excessive number of services. It's easy to stop fitting behaviour to appropriate class and instead stick them into meaningless services. This is when services become bad.

  2. What is an application service and how is it different from domain service ?

    An application service layer defines a set of services that act as boundary to the domain layer. Any interaction with the domain layer passes through these application services. The application services interface with domain and infrastructure layers to get the job done. The domain layer also can talk to infrastructure layer.

    Some excellent point about the distinction between the two can be found here and here.

  3. Can an application service directly talk to a domain service ?

    It depends on your preference on whether you want an explicit entity to own the operation or not.

    However one thing to be clear is that application service layer and domain service layer cannot be combined into one layer. It has been covered with good detail and explanations here and I will not go into them.

    My first take on this was that there is no reason for an application service to talk to domain service. However a colleague of mine had the view that if one service is going to call the other, then application service was not adding much value and it could pick up some of the work that domain service was doing.

    For e.g. our previous OrderAdjustService looks up a bunch of entities and calls appropriate methods on them. We got into an argument that if, this was all it did and had no special logic of its own then it may as well become an application service. However the methods being invoked in all cases were not exposed to application layer. So it was in this context we created a Order.adjust() method that delegated the call to OrderAdjust Service. Having Order.adjust() has an advantage that it makes Order own the adjust operation. If you are not particular about it, you could call OrderAdjustService from application service layer.

Domain services are an important part of Domain Driven Design since they give both flexibility and clarity to the model. As with all good things, use it with moderation !

Technorati Tags: ,

Subscribe to comments for this post

1 comment:

Anonymous said...

Thanks for the post. I liked it however I'm not so sure if it is a good idea calling the Service Layer (or Domain Service) from a Domain Object. I believe the Domain Object got the Service injected - how would this work if you Serialize and Deserialize the Object? e.g. use it in a clustered cache?


Clicky Web Analytics