A blog by Juri Urbainczyk | Juri on Google+ | Juri on Twitter | Juri on Xing

Sunday, July 1, 2012

Should Domain Objects contain business logic?

Recently I started a discussion with another software architect about where to implement the business logic in a SOA-like business layer. There would be service classes with methods which implement service functions like "bookFlight" or "upgradeFlight". We also introduced a "model" which consists of domain objects, holding the data to be manipulated by the services. The domain objects would also be stored in and read from a database and would serve as parameters for our service, meaning that they would be transferred via JSON or XML to the service consumer. Now, the question is: where should we implement the business logic? Should all of it be in the service functions or should all logic go into the domain objects or should we have a mix?

The picture below shows two services using two domain objects each. One domain object is used by both services. Should the logic be distributed? It can get even more complicated, since domain objects may also call one another, thereby using the logic of the other domain object.
But what is business logic anyway? Validation is often cited here, but that’s not all of it. More important are algorithms, calculations, rule evaluations and transformations which are driven by business requirements. Consider a domain object “passenger” which represents the customer of an airline. The passenger might have a method “getSignificance()” which returns the value of importance that the passenger has for the airline. This significance can depend on many things, for example his frequent flyer status, his age, his flight frequency, his marital status, the people he know at the airline etc. The algorithm to calculate this value also can be very complicated and it can change frequently. The significance of the passenger is so important, that it is calculated very often and it always refers to one single passenger. Makes it sense to implement “getSignificance()” at the passenger domain object?

The other solution would be to implement it in the service, e.g. in the “bookFlight” service, which uses it to calculate the price. But what if “upgradeFlight” also needs to know the significance of the passenger? Would you just duplicate the code and put it in the other service as well? That would lead to messy maintenance problems and a lot of discussion with the QA people (and for good reasons!).

Many people argue that domain objects without business logic would lead to a “clear separation of logic and data”. But, what are we talking about? It is just that, namely the combination of data and logic in objects, what the object oriented programming once set out to achieve. Thus, logic-less domain objects would rather indicate procedural programming, leading to something old-fashioned like a transaction script which is encoded in the service methods. It really reminds me of the old C programming style, putting all data in “structs” and just weaving some procedural code around it. But after all, we want to be object-oriented, don’t we?

The term “domain object” comes from “domain model”, meaning that all these classes make up the model which represents the business and problem domain. In object oriented modeling you simply identify all the objects which occur in the business domain and then you create one class for each object type found. Of course, the business domain not only contains information (“data”) – it also contains actions and activities (“methods”). Thus, domain objects naturally should contain logic as well.

Martin Fowler once coined the term “anemic domain model”
( for domain objects without logic. He calls it an “anti-pattern”. And I think he is right. But on the other hand, I don’t think that all the logic always should go into domain objects.

Domain objects should contain only the business logic, which is closely related to the data of the object and to the business entity’s meaning which corresponds to the current domain object. E.g., the basic logic to upgrade a passenger should not go into the passenger domain object (Although language seems to indicate that the passenger is upgraded that is not so. The booking is upgraded.). If there is a “booking” domain object the logic belongs there or into the service if not. Also, if many other domain objects are involved in the algorithm, this kind of logic should also go rather into the service class.

I would expect a service method implementation to look something like this:
myServiceMethod() {

     domainObject do1 = dao.fetchNeededDomainObject();

     if (do1.getSomething()) == someValue) {

         // some logic goes here    
         domainObject do2 = dao.fetchNeededDomainObject();
         do1.alter ();     

     // maybe some more logic goes here as well
     return do1;
As one can see, there is business logic in the service method. It is that logic, which is needed to coordinate the work of the domain objects and to drive the overall process. The services delegate most of their work to domain objects. Or, to put it another way, only that logic, which is not clearly bound to one domain object but rather to the overall process, should be found in the service. Nevertheless, domain logic must stay separate from persistence and presentation logic. This can be achieved easily e.g. by using the DAO pattern and by adding an extra presentation layer on top of the services. .

One further point: what about using the domain objects to transfer data? They could e.g. be transformed into JSON or XML and streamed to a client. Would that indicate that we should get rid of all the logic, since it could not be transferred so easily? But, as I just described, it is not the domain object that is transferred, it is a marshaled representation of it – meaning that only the data of the domain object is transferred. If the client was a Java client, the data could be un-marshaled into another domain object on the client, which would feature its method and business logic again.

So, I am absolutely in favor of solid object oriented modeling, with a well formed domain model. And this means, that domain objects must be able to hold logic, since otherwise they are nothing more than primitive data types in disguise.

No comments:

Post a Comment