http://separation-of-concerns.blogspot.com/ Bino B. has posted 2 posts at DZone. View Full User Profile

Business Logic in Domain Objects?

05.28.2010
| 19371 views |
  • submit to reddit

One of the design principles of any enterprise application which I found difficult to make up my mind about is Anemic domain models + Service Objects Vs. Fully featured domain objects.

Most of my business application layer consists of many Service Objects (DAOs, Session Beans, BOs, Delegates, Facades etc) AND many POJO domain objects. Here the domain objects are nothing but data holders. There is no business logic in it. Arguably, this is an application of "separation of concerns", where business and/or data access logic is separated from data holders (in other words, Transfer Objects or Value Objects). In my opinion, this also improves the testability of the code.

Whereas, there is a counter argument from another side (mostly the proponents of Domain- Driven Development (DDD)) that separating domain objects from its operations is nothing but going back to procedural style of programming. I don't know if I necessarily agree with that argument. Frameworks like Hibernate (even JPA) allows us to embed custom SQL's inside the domain model. In my mind that is a 'mixing' of concerns.

Also, imagine a method called sell() inside a 'Book' object. Does a book know how to sell it? Should it know? Also, Book has an existence without a sell() functionality. In cases like this, I prefer the business logic to be separated in to respective Service Classes which results in methods like BookServiceImpl.sell(book) and BookServiceImpl.calculatePrice(book) rather than Book.sell() and Book.calculatePrice().

This is not to totally dismiss the other argument. If we are developing software for a rapidly changing business domain, from a maintainability perspective, DDD approach may sound attractive. For example, when the way company sell books change , the place it requires the code change is most likely the Book.sell() method; where as, if we are to change the service method, it might affect various other sub-systems in which the service methods are re-used. But when Book object requires another domain object (e.g: 'CreditCard' object) to do its selling, things become complicated. Yes, we can inject CreditCard object into Book object, but effectively we are introducing a level of coupling and reducing testability.

 

Published at DZone with permission of its author, Bino B. Manjasseril.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jose Maria Arranz replied on Fri, 2010/05/28 - 2:20am

I agree with you.

Whereas, there is a counter argument from another side (mostly the proponents of Domain- Driven Development (DDD)) that separating domain objects from its operations is nothing but going back to procedural style of programming

This is FALSE, service objects can be fully object oriented, including class inheritance, encapsulation and polymorphism, in some way service objects are like Aspect Oriented Programming without special syntax constructs (ok, is not actually AOP).

 For instance: PersistBook service object can be the base class of JPABook or JDBCBook or XMLPersistBook, if you need these three approaches, try to do the same inside Book class... a complete nightmare.

 

Erdinç Kocaman replied on Fri, 2010/05/28 - 2:15am

DDD does not prohibit using service classes. If you believe a functionality belongs to the service layer, you can implement it in that layer. But if you want to implement it in domain layer, you can do it also. DDD gives you choice but anemic model is not that much flexible.

Mladen Girazovski replied on Fri, 2010/05/28 - 3:00am

Not sure if the author has actually read anything about DDD... you've got DomainServices in DDD, ApplicationServices and JPA Code for instance is inside repositories.

 

To me  it seems that the Author is mixing up the ActiveRecord Pattern with DDD, unfortunately he wouldn't be the only one to make that mistake.

Jose Maria Arranz replied on Fri, 2010/05/28 - 3:08am in response to: Erdinç Kocaman

Domain model behavior should be just about data coherence (for instance range check, null reference checking etc), and no much more, and sometimes is very difficult to check coherence in the domain model because some grouped actions bring your model to some invalid state temporally, in this case is almost impossible for the model to check, per change, whether the coherence is ok.

I'm not ranting against DDD, in fact DDD is the easiest way to model service layers around (thinking first about the model), I'm ranting against the opinion of "Anemic Domain Model" as an anti-pattern, regardless of who says it.

Mladen Girazovski replied on Fri, 2010/05/28 - 3:10am

Domain model behavior should be just about data coherence (for instance range check, null reference checking etc), and no much more, and sometimes is very difficult to check coherence in the domain model because some grouped actions bring your model to some invalid state temporally, in this case is almost impossible for the model to check, per change, whether the coherence is ok.

 Why not use the Specification Pattern and an Anti Corruption Layer? Those two are the Tools in DDD for overcomming the problems you've mentioned.

Ivo Limmen replied on Fri, 2010/05/28 - 3:39am

If we just, for a moment, forget about DDD and set aside patterns (and anti-patterns). It's best practice to keep your logic separately. How we call the package we put it in is irrelevant.

The logic is combinable with your model or you could put it in a separate 'service-layer'. You could even use a model package that contains simple POJO's, add a business package that has the business logic.

The reason we call business logic the domain is because that what it is all about. I's the core of the business you model in your software.

So moving your logic to a service layer is no problem as long as you keep all your logic in one package and keep it clean from external dependencies (except for POJO's) so that it is testable and maintainable.

And if people call it DDD or not... that is not important.

Jose Maria Arranz replied on Fri, 2010/05/28 - 3:44am in response to: Mladen Girazovski

Right but I'm talking about adding this kind of stuff inside the data model when you change something in the model.

 

Mladen Girazovski replied on Fri, 2010/05/28 - 3:59am in response to: Jose Maria Arranz

I see your point now Jose.

Mladen Girazovski replied on Fri, 2010/05/28 - 4:07am in response to: Ivo Limmen

If we just, for a moment, forget about DDD and set aside patterns (and anti-patterns). It's best practice to keep your logic separately. How we call the package we put it in is irrelevant.

The logic is combinable with your model or you could put it in a separate 'service-layer'. You could even use a model package that contains simple POJO's, add a business package that has the business logic.

The reason we call business logic the domain is because that what it is all about. I's the core of the business you model in your software.

So moving your logic to a service layer is no problem as long as you keep all your logic in one package and keep it clean from external dependencies (except for POJO's) so that it is testable and maintainable.

And if people call it DDD or not... that is not important.

In fact you're talking about non-OOP if you seperate the logic from the data, cause that's what OO is about, combining the data and logic that belongs together, that is why the "anemic domain model" (stupid pojos with getters & setter but hardly any business logic) is something that reflects the procedural programing paradigm, not OOP.

Nick Wiedenbrueck replied on Fri, 2010/05/28 - 4:07am

I've always had the feeling that there is something flawed with this traditional approach to object orientation in business modelling as we are taught. I mean object orientation is all about mapping the real world appropriately to classes, but I've never seen a real book that sells itself. Selling a book is more of a process that does something with a book (procedurally). But in most examples in literature books would get a sell() method.

On the other hand, in technical "domains", for example when developing a framework, object orientation really works so much better than in business domains.

Wojciech Gdela replied on Fri, 2010/05/28 - 6:30am

Book does not sell itself. A Salesman does. So you shold implement Salesman.sell(book), possibly accessing CreditCard object in this method. The Salesman IS a part of domain model, although it is not persistent object.

Take a look at this blog post to see an example how you can make your Book rich of business logic and get rid of procedural BookService class

Bino B. Manjasseril replied on Fri, 2010/05/28 - 8:41am

When designing a system, I like to think in terms of possible domain objects and I like the fundamental idea of DDD, but this article was basically to construct a difference of opinion on the suggestion that "Anemic Domain Model is an anti-pattern". (Same link Jose Maria used in his comment)

The problem with 'obese' domain objects is that it violates one primary OO principle: Single Responsibility Principle (Separation of concerns).

More than the question of whether a Book can sell itself or not, I would ask 2 basic questions to myself when I add a new functionality to the Book class(or any class for that matter):

1. Can this responsibility be separated? 2. What makes the code more testable?

Mladen Girazovski replied on Fri, 2010/05/28 - 8:55am

The problem with 'obese' domain objects is that it violates one primary OO principle: Single Responsibility Principle (Separation of concerns).

What do you mean exaclty?

It is actually a fundamental principle of OO, that logic and data goes together, if they belong together.

If you seperate logic from data, you're back at procedural programming!

For example in C, you'd use struct for the data and functions for the logic, in Java you'd have a service with all the logic, and dumb POJOs with getters and setters.

 

Daniel Tuchtenhagen replied on Fri, 2010/05/28 - 9:01am in response to: Nick Wiedenbrueck

@Nick Wiedenbrueck

It is not about, that the book sells itself. But what if the book contains a collection of pages and you want a getPageCount() method somewhere? Would you add an integer to the Book class (not DRY) or add the getPageCount(Book book) to the BookService or would you always ask for the page collection object in a such an anaemic class (Law of Demeter problem)?

In a POJO you can just encapsulate this in a  getPageCount() method you add to the Book class.

Jose Maria Arranz replied on Fri, 2010/05/28 - 10:05am in response to: Mladen Girazovski

Are you saying that PersistBook class holding (encapsulating) a Book object like data alongside JPABook class (extends PersistBook) and JDBCBook (extends PersistBook) including base methods, abstract methods and redefined methods with polymorfism... is procedural programming?

Putting too much in the same class is the kind of bad object oriented design that inspires the bad name of OO for some (unexperienced) people. For instance some people think the myth "implementation inheritance is bad", this wrong conclusion (kind of "all of nothing" reasoning) is usually motived by abusive use of inheritance, that is, when there are too many concerns in the same inheritance tree (usually a data model tree doing too many things).

The typical book of OOP showing the typical three class example with inheritance including some data and some related behavior in the same classes is fine to teach OOP basics, and this is OK, but in a real world application with several ways of persistence (database, file read/save with several formats), complex use cases and complex views the typical approach of putting all of this stuff in the data model is crazy and the worst kind of OOP programming.

 

Mladen Girazovski replied on Fri, 2010/05/28 - 11:16am in response to: Jose Maria Arranz

Are you saying that PersistBook class holding (encapsulating) a Book object like data alongside JPABook class (extends PersistBook) and JDBCBook (extends PersistBook) including base methods, abstract methods and redefined methods with polymorfism... is procedural programming?

 I have no idea how you arrive at these statement, i have never mentioned anything about a PersistBook Class, JDBCBook or anything the like.

But to pick up your example, why would anyone want to have a "JDBCBook" or "JPABook" class?

I'd say that this would be a really bad example for an OO Model, a Book should not care if it is stored by pure JDBC, JPA or some other mechanism. This is where SoC is important, different aspects/concerns in different places.

Putting too much in the same class is the kind of bad object oriented design that inspires the bad name of OO for some (unexperienced) people. For instance some people think the myth "implementation inheritance is bad", this wrong conclusion (kind of "all of nothing" reasoning) is usually motived by abusive use of inheritance, that is, when there are too many concerns in the same inheritance tree (usually a data model tree doing too many things).

 Well, implementation inheritance is not a bad thing as such, but it creates strong coupling, in some cases this is ok, in others it is not, composition on the other would be a solution that has less coupling.

 The typical book of OOP showing the typical three class example with inheritance including some data and some related behavior in the same classes is fine to teach OOP basics, and this is OK, but in a real world application with several ways of persistence (database, file read/save with several formats), complex use cases and complex views the typical approach of putting all of this stuff in the data model is crazy and the worst kind of OOP programming.

Lets say we turn the Book example into somthing more like DDD, there would be a book class, that would know about its pageCount , title, etc. (actually a bad example for DDD if there is no "Book" Domain Expert around ;) ). A domain service would take care of selling books (please note the diff. to an app. service, the domain service is part of the domain), a repository takes care of persisting and retrieving books, an application service would make this actions available from outside the core app (ie. for the UI).

 

Jose Maria Arranz replied on Fri, 2010/05/28 - 11:33am in response to: Mladen Girazovski

I don't understand your position, sometimes you like separation of concerns (it drives to anemic data models) and sometimes blame about this kind of separation because data model is not OOP (because is almost fully naked of behavior)  :)

 

Bino B. Manjasseril replied on Fri, 2010/05/28 - 11:56am

It is actually a fundamental principle of OO, that logic and data goes together, if they belong together.

But when this principle violates the principle of SRP (Single Responsibility Principle), SRP takes precedence because SRP is what makes the code maintainable.

If you seperate logic from data, you're back at procedural programming!

It depends on how you define 'logic'. Simple logic such as making the attributes more client-friendly is no problem (e.g: getters, setters, concatenating few attributes, sum of few decimal attributes etc). But as we introduce more complex 'logic' (persistence, operations which depends on other domain objects or service objects), we are reducing maintainability. Any time the domain object becomes more than a Transfer Object, there is room for refactoring (extract class).

Axy Kazry replied on Fri, 2010/05/28 - 12:17pm in response to: Bino B. Manjasseril

I don't think this is an either-or situation. OO states that data and logic about that data should be organized in nice, self-contained packages that we call objects. The crucial word in this sentence is "self-contained".

When you look at a domain model, there is logic that is circumscribed to an object. Example: Book has validation logic that is unique to book (make sure that the number of pages is positive, that the ISBN number is valid, etc.). This kind of logic only requires access to the attributes of Book, its superclass attributes, and maybe some data in associated objects. Book may also have additional behavior whose scope is restricted to Book. This kind of functionality, whose scope is limited to a single class, is best implemented in the class as class or instance methods.

On the other hand, there is functionality that requires cooperation between multiple objects. The process of buying a book involves multiple domain classes: Book, Salesman, Shipper, etc. It makes sense to put that kind of logic into one or more service objects.

This is really no different from refactoring common functionality into a superclass or a helper class. In fact, you may start off by putting a method inside your domain object then refactor it into a service classor vice-versa.

 

Jochen Bedersdorfer replied on Fri, 2010/05/28 - 6:12pm

There are two main approaches here and they have different objectives and trade-offs.

 Data-Driven - open up your data, make it available as resources, have transformations work on these resources.  Best suited for REST-based application. Use a proper datastructure for your data. Using Classes for this just means you will have fun refactoring your code later on when your data changes or evolves. So a service layer with dumb POJOs is not very effective. Choose a better datastructure instead that easily evolves like XML or JSON. Your book selling example would probably consist of a /book resource which is referenced by URL from a /sale resource which someone has created by POSTing to a /sales resource...

 

Object-Oriented - domain-driven modelling of your problem domain. In this process, if you start thinking about a service layer, you are doing something very wrong. BookServiceImpl? No way. As mentioned above: You should probably introduce a Salesman class. And very often, if you think of processes in terms of verbs like sell(book), it is helpful to express this as a noun Sale(book), which fits nicely into your domain-object model. If you model your domain well, changes will be minimal, your data is nicely encapsulated and you can probably auto-generate a UI for that. However, this only works well if everyone speaks your fav object-oriented language (Server/Client/3rd Parties). Exposing an OO domain model through web-services has seen one spectular failure (CORBA) after another (SOAP) and has no place in scaleable, massive, multi-platform, multi-language applications.

 

 

Alessandro Santini replied on Fri, 2010/05/28 - 6:58pm

 I am seriously wondering if - asking what does Object-Oriented Programming mean - I would get a different answer by each user.

Seriously gentlemen, I am outraged. Do not take this post as a flamebait, although I accept it does sound like.

"DAOs, Session Beans, BOs, Delegates, Facades, ..." - the list could go on nearly endlessly. There are plenty of architectural blueprints out there and the location of a method raises so many questions?

Do not misunderstand me - I am not questioning your experience and skills - they surely are far superior to mine - but seeing that the location of a method can spark such a long thread makes me wonder about the productivity of object oriented programming languages.

 

Mladen Girazovski replied on Fri, 2010/05/28 - 9:00pm in response to: Jose Maria Arranz

I don't understand your position, sometimes you like separation of concerns (it drives to anemic data models) and sometimes blame about this kind of separation because data model is not OOP (because is almost fully naked of behavior)  :)

 I think that your & the authors understanding of SoC is simply wrong.

SoC doesn not mean to seperate logic from data, it means to put different responsibilities in different places.

SoC & putting logic & data together are both basic OO principles that do not contradict each other.

 

Mladen Girazovski replied on Fri, 2010/05/28 - 9:07pm in response to: Bino B. Manjasseril

But when this principle violates the principle of SRP (Single Responsibility Principle), SRP takes precedence because SRP is what makes the code maintainable.

 Again, SRP, SoC etc. do not mean to seperate logic from data.

 It depends on how you define 'logic'. Simple logic such as making the attributes more client-friendly is no problem (e.g: getters, setters, concatenating few attributes, sum of few decimal attributes etc). But as we introduce more complex 'logic' (persistence, operations which depends on other domain objects or service objects), we are reducing maintainability. Any time the domain object becomes more than a Transfer Object, there is room for refactoring (extract class).

Refactoring also applies to rich domain models, if you try to keep logic out of the data, then you can refactor, but also if you try to put the logic to the to data it belongs, you can refactor.

Persistence logic is not business logic and those two should not be put together, SoC.

See what Martin Fowler has to say about the TransactionScript pattern and how he compares that to the DDD approach, unfortunately the online version is way to short to be useful imho.

 

 

Jose Maria Arranz replied on Sat, 2010/05/29 - 8:24am in response to: Alessandro Santini

but seeing that the location of a method can spark such a long thread makes me wonder about the productivity of object oriented programming languages

Do you have a better alternative (a better paradigm)? In the very short term copy and paste is by far the most productive approach.

The length of this thread and some statements make me think seriously about how much good OOP software is being done out there.

"DAOs, Session Beans, BOs, Delegates, Facades, ..." - the list could go on nearly endlessly. There are plenty of architectural blueprints out there and the location of a method raises so many questions?

When a big and complex software takes more than 1000 classes/interfaces with no code duplication you can figure the number of questions you have had to ask yourself and your team. This management ART of 1000 classes/interfaces (and upper) is the THING that makes this job SO AMAZINGLY INTERESTING AND SO AMAZINGLY COMPLEX AND UNFORTUNATELY UNDERVALUED.

Take a look to NetBeans code or any other complex software to understand why so many artifacts "DAOs, Session Beans, BOs, Delegates, Facades, ..." exist.

 

Jose Maria Arranz replied on Sat, 2010/05/29 - 8:23am in response to: Mladen Girazovski

See what Martin Fowler has to say about the TransactionScript pattern and how he compares that to the DDD approach, unfortunately the online version is way to short to be useful imho.

Yes I know Martin Fowler is a valued man in the software industry, but I'm a scientific man and the "principle of authority" does not work very much for me, and because the "anemic data model" is considered an anti-pattern by Martin, I'm sorry but I'm no very confident about statements like "this is good because Martin Fowler says it".

 

Mladen Girazovski replied on Sat, 2010/05/29 - 8:36am in response to: Jose Maria Arranz

Yes I know Martin Fowler is a valued man in the software industry, but I'm a scientific man and the "principle of authority" does not work very much for me, and because the "anemic data model" is considered an anti-pattern by Martin, I'm sorry but I'm no very confident about statements like "this is good because Martin Fowler says it".

 I never said it's good because Fowler says so, what i tried to make clear was, that Fowler himself also advocates the "anemic domain model" in certain cases, he calls it the "TransactionScript Pattern", and since the Author of this Artcle linked to Fowlers view on the anemic domain model, it should be at least noticed how he would use the anemic domain model approach himself.

A statement like "all anemic domain models are bad" is as true & valuable as the stament "all rich domain models are wrong", its just way too general to be taken seriously, there is a place/project/context for nearly every possible approach.

If people are really interested in DDD and rich domain models i would recommend Eric Evans Book on DDD and the DDD Yahoo group, i still doubt that the we are all talking about the same thing when we say DDD, i think that most people mix up DDD with the ActiveRecord pattern, where the entity itself take care of the persistence logik.

Alessandro Santini replied on Sat, 2010/05/29 - 1:15pm in response to: Jose Maria Arranz

I think I have so many points that a reply to your post would not be enough - I think I should write a whole post about this.

However, let me briefly summarizes:

  • OOP is not a method to avoid code duplication - indeed you can duplicate as much code as you would in the procedural world; OOP is a different way of modelling a business, through means of objects instead of functions and data structures.
  • The world has not started with Java and there are many nice and complex application, in production since many years (unlike Java applications being re-implemented at every trend change), and that are normally maintained and evolved as any OOP-counterpart;
  • Software architecture has not been originated by OOP - patterns are not something originally created for to the software world and particularly to the OOP world (although the phenomenon gained momentum with OO languages)

When a big and complex software takes more than 1000 classes/interfaces with no code duplication you can figure the number of questions you have had to ask yourself and your team.

You are giving for granted that that application actually requires more than 1000 classes. There's a common phenomenon, particularly dear to OO architects and apparently hard to die, called over-engineering. JEE (JSF in particular) and to a very broad extent the JDK are a good example of what I mean.

The length of this thread and some statements make me think seriously about how much good OOP software is being done out there.

To me, it makes me think about how much confusion there is in people's mind. There are so many other fields of engineering where there is a common acceptance of methods; software, particularly OO's - cannot reach a "steady state".

This management ART of 1000 classes/interfaces (and upper) is the THING that makes this job SO AMAZINGLY INTERESTING AND SO AMAZINGLY COMPLEX AND UNFORTUNATELY UNDERVALUED. 

Here is the point - management of 1000's of classes should be a process, an engineering process, and not *art*. Let me point out one thing: this job is so amazingly complex because - in most cases (and I refer to the thousands of relatively simple CRUDS around) -Java engineers deliberately want to make it complicated to make their jobs safe.

 

Jose Maria Arranz replied on Sat, 2010/05/29 - 2:37pm in response to: Alessandro Santini

I agree with most of your statements, but OOP is an approach "on top" of procedural programming, is not an alternative.

You are giving for granted that that application actually requires more than 1000 classes. 

It depends on the project. For instance if you says me that NetBeans source code is 100 classes be sure I am going to say with no doubt "what pice of crap", I don't need to see the source code, because NetBeans project (same Eclipse, same many other) is SO COMPLEX that anyone can figure the very big number of classes needed to build a good architecture.

Of course a big number of classes does not ensure your program is fine specially when most of classes are doing the same or as you say it suffers of over-engineering.

 Here is the point - management of 1000's of classes should be a process, an engineering process, and not *art*.

Definitely is not an engineering process yet when we are discussing something SO basic as "anemic data models good or bad?".

 

Carlos Rodríguez replied on Sat, 2010/05/29 - 5:36pm in response to: Bino B. Manjasseril

I think you take the SRP too far when you say that putting data and behaviour together is a violation to the principle. When you create getters & setters for a property, you are introducing 2 responsabilities: reading a property and writing a property. Is that a violation to SRP? If you are developing a typical CRUD application, with simple validations, then an anemic domain model is just fine. But when you have complex business rules, a rich domain model is probably a better option. (Sorry for my poor english, i'm learning).

Stephane Vaucher replied on Sun, 2010/05/30 - 2:56pm in response to: Jose Maria Arranz

"I agree with most of your statements, but OOP is an approach "on top" of procedural programming, is not an alternative." Hmmm, not quite. I guess you haven't studied comp. sci. It is a response to the procedural paradigm. The first OO language was Simula67, a language that was created to simplify simulation. To describe different entities, the language combines state and behaviour. This combination did not exist in procedural code before, and that is the underlying motivation for OOD. I haven't looked at the Netbeans code base, but I've used the Eclipse code base quite a bit. The overall design is understandable, and most methods are placed relatively intuitively. I'll give an example of method placement from the JDT: The class MethodDeclaration describes a method from the AST representing a Java class. This class allows a coder to access the name, the parameters, the return type, etc., of a method -> This is obviously domain information. It also declares a method to resolve its bindings (to transform string identifiers to references to other objects). This requires computation as it verifies what class declared the method, the types of parameters, etc. -> You could argue that this should be in an external class, but it is much simpler to keep this information in this class as resolution methods need to know about the method's internal (and hidden) state. From your statement/the author's statements, the resolution functionality should be moved elsewhere. This has however not been a problem for the development and maintenance of eclipse. Furthermore, it is placed in an intuitive location helping programmers use this code. Consequently, I would argue that this is a good placement. Please do not use unjustified blanket statements like "domain model behaviour should be just about data coherence". Behaviour should be placed where it makes sense, not according to strict guidelines.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.