Single Responsibility and Object Oriented Programming

241 views
Skip to first unread message

Alexander Wirz

unread,
Apr 1, 2014, 1:41:47 PM4/1/14
to clean-code...@googlegroups.com
Yesterday I read this article http://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/. The author claims that an anemic domain model where "business objects" contain only data and the logic is scattered across some services is more solid than a rich domain model which is a solution in a more object oriented manner where an object contains both the business logic and the data. This is not the first time I hear or read this argument. People are saying that putting both the logic and the data inside one object violates the Single Responsibility Principle because such an object would have at least two responsibilities: "to model the business data and implement the operations on that data".
I disagree with this point of view. I don't think that "to model the business data" is a responsibility in terms of SRP. Why would you model the business data without logic that operates on that data? Data without the corresponding logic is in my opinion rather useless. And if you have to change the logic you sometimes also need to change the data model but why would one change the data model without the need of changing the business logic?
What do you think about this? Does putting data and logic together in a single object violate the Solid Principles? Can someone provide an example where separating the data and the behavior would result in a more readable or more maintainable code?

David Hunt

unread,
Apr 1, 2014, 6:48:47 PM4/1/14
to clean-code...@googlegroups.com
I read that article too. I found it because I am constantly writing anaemic domain models and wanted to know where I was going 'wrong'.

I didn't find the author's arguments particularly enlightening either. I agree with you that data is purely a function of system behaviour and therefore need not be separated.

But I have had success with my non-OO style of programming. It's usually event based and very procedural.

For example... Say I'm handling an event like a http request to a web server. Standard MVC pattern to separate my app from the concerns of the web and view rendering. My interactor will contain all the application's logic for this feature and use simple data structures like Users and Products and so on.

Now in this example I'm adding a product to a persisted user basket or shopping cart. I could implement this:

var user = userService.getUser(123);
user.AddToBasket(selectedProduct);

That's the OO version. Or this:

var user = userService.getUser(123);
userService.AddToBasket(user, selectedProduct);

That's my rubbish non-OO version. In the first we have a Rich Domain User Object with both data and behaviour. In the second we have an anaemic User object with just data, and all the behaviour in the service.

What doesn't make sense to me is having the User object know about persistence or any other third party system? Even a simple getAge function would require my User object to know about reading the current date from somewhere. Why should the user object know about databases, and the OS and web services and goodness knows what else?

In the second version you have the option to split these various concerns across different services or interactors, and you have a simple shared model for the data that is used between these different behaviour encapsulating classes.

Which of the two versions is more testable? Which is easier to follow? I find that the 2nd option is.

Maybe I just really don't understand OO. As I said, I am currently searching for guidance myself. But you wanted an example, and perhaps this is one.

Michel Henrich

unread,
Apr 1, 2014, 9:22:59 PM4/1/14
to clean-code...@googlegroups.com
@Alexander,
I think the author of the article is correct when he says that the persistence logic must be split from the domain model. But completely stripping any kind of action from such a business-rich object such as Customer or Order, is no different than writing procedural code with Data Structures and Functions. Not that structured programming is bad, everything has its use, but this is somehow being advocated as "good OO", so, at least to me, it seems like there is a contradiction somewhere. Anyway, Martin Fowler surely expresses this better than me in his article: http://www.martinfowler.com/bliki/AnemicDomainModel.html.

@David
In my opinion, we should model the system based on the intents - on the business needs behind each action - and that we should group these actions by using the names that most clearly reflect the business.

So, instead of thinking about Domain vs. Services, let's think about plain business rules. Let's forget about the "big names".

From the action of "adding a product to a user's basket", I imagine Product, User and Basket objects, and one or two Add methods (basket.add(product) and maybe user.addToBasket(product), if necessary). The action is a plain business rule, and the objects within this application directly reflect the business. If there are other rules related to these objects, I would expect to see methods for them as well.

Now, as for persistence. the business may require that the Product, the User and the Basket are available for later use after some kind of shutdown. There usually isn't a direct business correlation for this kind of mechanism, except maybe for a file cabinet full of paper files and folders.
Then, we come up with our own names: Database, Storage, Repository, Gateway, FileSystem, whatever. In any case, the name represents a persistence mechanism, and just like for Product, User and Basket, I imagine a Gateway object with a Save method, that directly reflects the business need. I wouldn't imagine that a Product should persist itself, just as I wouldn't imagine it to print itself.

So, to link with your examples, I'd go with the "user.addToBasket(product)", regarding the specific business rule, and a "userGateway.save(user)", with regards to persistence. This design splits the responsibilities, so it's easy to test, and also easy to follow, since we have defined an obvious place to put each action.

vivek poddar

unread,
Apr 3, 2014, 12:43:03 AM4/3/14
to clean-code...@googlegroups.com

Hi,

I think @david's second approach is more clear and flexible, because in my opinion your use case must be independent of business models. The business models should be responsible to provide the data(which should be independent of the underlying db) to the so called 'services' which then use those data to do some calculations. For example, what if I want to add a functionality to give some points to user based on some specific purchase amount done by him, in this case we can write a simple service like:

points = pointService.getPoints(user_model, shopping_amount)
pointService.updatePoints(user_model, points)

In the above scenario we are just doing DI for user_model.

I am just a newbie in this field but hope I am able to keep my thoughts clear. I have read something about DCI and now I am being interested in this approach (http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html) . 

Do let me know for any suggestions.


James Green

unread,
Apr 3, 2014, 12:21:05 PM4/3/14
to clean-code...@googlegroups.com
The terminology here is too broad. "user" from a "userService" means nothing to anyone beyond a bunch of data for a user.

Imagine instead a "Shopper" which extends "User" by adding basket functionality. "User" by itself is purely data held about users. A "Shopper" knows how to shop and is a "user" of the system.

"Shoppers" have order histories and a basket associated with them. "Shoppers" end up at a "Checkout" where they "pay()".

Sure you can do this through services but shouldn't the services be the glue holding the domain with the infrastructure? Akin to BSkyB PLC providing a television service that supplies "Channels" to my "Television" I don't ask Sky to change channel, I ask my television.



--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Alexander Wirz

unread,
Apr 4, 2014, 12:59:04 PM4/4/14
to clean-code...@googlegroups.com
Thank you all for your answers!
 @david I fully agree with you about the persistence thing. Persistence code inside the User object obviously violates the SRP. It looks like the author uses the active record (anti)pattern. And active record has its problems. It usually creates a strong coupling between the domain logic and the persistence mechanism, which is not a good thing (especially for unit testing). But it's not necessary to put the persistence code in the domain objects (or in a base class for the domain objects). It's even better to ignore the persistence mechanism while designing the domain (business logic). DDD for example has an aspect called "Persistence Ignorance": http://devlicio.us/blogs/casey/archive/2009/02/12/ddd-there-is-no-database.aspx.
But my question was not about the persistence. Let's imagine we don't need a database :)

@vivek Can you explain why you find 'userService.AddToBasket(user, selectedProduct);' more clear and flexible than 'user.AddToBasket(selectedProduct);'? To make sure that we are speaking about same things, what exactly do you mean by "use case" and "business model"? Can you provide an example for both? I believe you can separate both using object oriented design. In the example provided by @david the userService must know about the user and the selectedProduct. Maybe User and Product are also used by other parts of your program. What if you have to change the business rules for a user or a product? Will you have to make changes in each place these classes/data structures are used? I think it makes the maintenance more difficult because you don't even know where your user "class" is used.
BTW thanks for the DCI link. You may also find this one interesting: http://www.eventuallyinconsistent.com/2012/01/changing-mindset-part-1-4-classic.html

Alexander Wirz

unread,
Apr 4, 2014, 1:21:26 PM4/4/14
to clean-code...@googlegroups.com
@James I think you made a great point about the terminology. "user" is probably to ambiguous. It may lead to a bloated class with to many responsibilities.
I really like your idea about Shopper or Checkout classes. I can imagine that it will lead to a much cleaner design and you will less likely end up with a 1:1 mapping of classes to database tables :)
Reply all
Reply to author
Forward
0 new messages