1

I am refactoring an e-commerce web-application, currently working on the UserBasket class, which will have to handle adding or removing items, changing their quantity, getting total amount, total number of items etc.. When displaying the basket, the item's name, description and main-image-filename are also needed.

The UserBasketthen has to know the price of the Item for the given quantity. Different methods of calculating the current applicable price should be swappable. I could employ the Strategy pattern either in some sort of ItemPriceGetter with which the UserBasket would be constructed, leaving the item unaware of this, or construct the Item itself with a PriceFindingStrategy.

I have reservations against a completely anemic domain model. An item should be able to tell me its price for a certain customer and quantity on a certain date - it should be able to tell the filename of its main image, its availability etc.

Essentially, in the database, items (and other domain entities) have multiple 1-to-n relations with sometimes complex and varying logic to find the currently applicable one among them (for price, image, description etc).

If I don't want an anemic domain model, but there will be different strategies for getting the 'right' related information, how do I avoid constructor over-injection when there are multiple such 1-to-n relations from an item where a strategy to chose the right one is needed.

An Item-Class is there to tell you about data relating to items, and perform business logic that is centered around them - so making it able to tell you its price for a given customer and quantity, its main-image-filename or some such doesn't seem like a violation of the Single Responsibility Principle.

With an anemic domain model, I would have the logic neatly divided and encapsulated, but now, for - say - an itemlist to find out availability, price, name and image of an item, it would need not just an item, but also an ItemPriceFinder, ItemAvailabilityFinder, ItemImageFinder (or some such) - cohesion would (seemingly) suffer, and things that properly belong to the item are no longer encapsulated there.

With a rich domain model, the item itself will have to have an ItemPriceStrategy,ItemAvailabilityStrategy, ItemImageStrategy or similar. So it seems I'm stuck with either injecting a lot of dependencies or losing coherence and making my domain model anemic... and in the latter case, I would still have classes (which e.g. need a lot of info about Items) which I would have to construct with the Domain Object itself and most of the services to get related data.

What is the best practice to deal with such a situation? Specifically: How should the different ways of getting and filtering the related information for one kind of business entity based on contextual parameters be encapsulated and how should it be integrated so as to maximize coherence and avoid violating the single responsibility principle?

8

I would go with the anemic domain model here. Consider the example of your ItemImageFinder. Consider a few scenarios:

  1. You decide to extend your system to also include Image thumbnails, or including alternate images.
    • Now you need to modify your Item behavior. Clearly this is not the only reason for Item to change, so you are violating SRP.
    • You either have to create a behemoth method, or multiple different methods for image behavior.
    • With a ItemImageFinder, all these changes would be in a class relating to Images, not the entire item.
  2. You need to create a command line tool.
    • Now your items have to load up all this image data, which it cannot even render.
  3. You decide to have multiple implementations of the Item interface.
    • Perhaps one of the implementations doesn't need to look up Items. Now you have all this behavior you don't need.

Also, I am not sure why you want the ImageFinder class to be ItemImageFinder. It could just be ImageFinder, with a method getImage(Item). It could then be extended to have a method getThumbnail(Item) or a method getImage(SomeOtherObject).

You said yourself "cohesion would (seemingly) suffer." Firstly, I don't agree that it does. But forget about "accepted wisdom" for a second: Even if it did, what exactly would be the drawbacks to such a cohesion breakdown? I can't think of any.

  • +1: You beat me to it. I wouldn't have this stuff hang off the item, I'd pass the item to providers that would give me back the right things for this item. – Binary Worrier Mar 23 '15 at 15:30
  • I'm on board with not having the actual code inside the Item - it should be swappable. Hence the idea was to use strategies. The Item would be constructed with strategies to get its price, availability etc - the item's methods to get this data would just call the methods of the appropriate strategy with which it was constructed. With an anemic model the drawback I'm worried about is that the flexibility is the same (strategies either way), but now, to get data belonging conceptually to one class, I need further objects, and can't initially know when a provider will be needed and when not. – Michael Bauer Mar 23 '15 at 15:50
  • @MichaelBauer Why do you need to know that initially? If you need a behavior, just add code to inject that provider. – durron597 Mar 23 '15 at 15:52
  • @durron597 I meant the simple rule "if the data conceptually belongs to class X, you can always get it from class X" would no longer apply - making it necessary for devs to think about which data-requests a business entity can fulfill itself and which would require additional providers. – Michael Bauer Mar 23 '15 at 16:01
  • 1
    Yay for anemic models. Vastly underrated, IMO. – Robert Harvey Mar 23 '15 at 18:59
3

An Item-Class is there to tell you about data relating to items, and perform business logic that is centered around them - so making it able to tell you its price for a given customer and quantity, its main-image-filename or some such doesn't seem like a violation of the Single Responsibility Principle.

Yes it is a violation of the SRP. It is okay if the item knows its main-image-filename, and its base price, but it is not okay if the item has knowledge of customers so that it can tell you its final price for a given customer.

As a rule of thumb, ("a principle with broad application that is not intended to be strictly accurate or reliable for every situation",) the same item class should be viable in a manufacturing application instead of a sales application, where there would be no notion of a customer. Such a class could perhaps contain some extra information which would be unnecessary in another application, (like materials necessary to manufacture, or time to manufacture,) but at least it would not fail with a class not found: "Customer" error.

To think of it from another point of view, why should the item class be able to tell you how much it would cost for a certain customer, instead of the other way around: a customer class being able to tell you how much a certain item would cost for it. Whenever you come across dilemmas like this, you know that neither class is the right place for the logic.

As another rule of thumb, classes should only contain logic which deals with classes that they are immediately related. So, the customer class will presumably have orders, so of course it is okay if the customer class contains logic that can tell you, for example, what is the total sum of all orders ever placed. This makes the customer class non-anemic. But the item class probably would not have any immediate relationships (in either direction) with the customer class, so this probably means that there should be no logic in either class involving the other. If this means that the item will have to be anemic, so be it.

In my experience, when you have a piece of logic that you want to put somewhere, but there appears to be no model entity which is the one right entity to contain this logic, it is best to move this logic to the "model" object, which by definition knows all the entities and the relationships between them. This tends to result in a quite fat main model class, which is the opposite of anemic, if that's any consolation.

  • Knowing about customers would certainly be a SRP-violation. I would simply have the item receive the customer-no and quantity as a parameter for its getPrice-method, which the item would simply pass through to the PriceStrategy it was constructed with - no need for the item to have any inappropriate knowledge. But because all this info I want is info about the item - I would still ask the item. If I use provider-services - can I avoid constructor over-injection for objects which need data from different providers for the same business entity? Facades? – Michael Bauer Mar 23 '15 at 15:58
  • Constructor over-injection is essentially dependency over-injection, and it tells you that you have too many dependencies, which in turn means that you are over-violating the SRP. Injecting the dependencies via another mechanism will not fix this. I think you need to reduce the dependencies themselves. – Mike Nakis Mar 23 '15 at 16:09
  • To use a concrete example - at some point I will need to display items. For this, I need their no, name, price for current customer, availability, main-image-thumbnail and canonical URL (at least). A class which displays such itemlists cannot do without these dependencies, and thus needs to get the data either from the item itself or from the item plus a set of providers - or am I missing something fundamental? – Michael Bauer Mar 23 '15 at 16:18
  • Right, there will need to be such a class, which will still, though, have a single responsibility: to create a data set for a list control, by aggregating information provided from other classes, whose responsibilities are to contain and/or calculate that information. – Mike Nakis Mar 23 '15 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.