7

Given:

  1. Spring MVC - Hibernate.
  2. Controller -> Service -> DAO
  3. Now I have a method which retrieves something from the DB and EVERYTIME it does this, has to do another method say "processList" (something like just changing some values in the list depending on some screen parameters).

Question:

  1. What layer do I put this "processList"? (Controller, Service or DAO? and why)

I really need some j2ee clarifications now, I know that MVC is the same across languages but I just need to be sure :) If I am doing this in .net I would have undoubtedly put this in service.

tereško
  • 58,060
  • 25
  • 98
  • 150
Rey Libutan
  • 5,226
  • 9
  • 42
  • 73
  • 1
    This really, really depends on what the method `processList` is doing from the logical view. – Pavel Horal Jun 16 '13 at 07:39
  • 1
    Definitely **not** in controller. I would put it in the service, but there might be a huge difference between what you (or java community in general) understands as service and what my perception of service's responsibilities are. – tereško Jun 16 '13 at 08:12
  • I think this question falls into the *too general* SO question category (i.e. it is a candidate to be closed). Nevertheless I've added a bit lengthy description of what are the rules I am trying to follow when facing decisions like "where this piece of code belongs". – Pavel Horal Jun 16 '13 at 08:29

1 Answers1

19

This really depends on what processList is doing exactly. There is no golden rule. Some rules I try to follow are:

  1. Never make calls between main objects on the same layer.
    • ManagementServiceImpl should never call NotificationServiceImpl.
  2. Don't make circular dependencies between objects.
    • This is very similar to the one above.
  3. If you find yourself repeating some logic across multiple main object, try to restructure the code and extract it in specialized logical classes (this will improve unit-testing as well).
    • E.g. UserUpdateHandler or NotificationDispatcher (these are still owned by the service layer -> noone else is allowed to call them)...
  4. Put the code where it logically belongs.
    • Don't get distracted by the fact, that some class needs to do something. It might not be the right place for the code.
  5. Never write fully generalised code before you need to.
    • This has its term as premature generalisation, which is a bad practice similar to premature optimisation. Saving few lines of code now can lead to pulling your hair out in the future.
  6. Always write code which is able to become generalised.
    • This is not a contradiction with the previous. This says - always write with generalisation in mind, however don't bother with writing if it is not needed. Think ahead, but not necessarily act ahead.
  7. Leave business logic to service layer, data persistence logic to data layer and user interaction logic to presentation layer.
    • Don't try to parse user input in service layer. This does not belong there similarly as counting final price in e-shop application does not belong to presentation layer.

Some examples for processList:

  • Example I - fetching additional relations via Hibernate#initialize
    • This is something which is really in between the service and DAO layer. On older projects we had specialized FetchHandler class (owned by service layer). In newer projects we leave this completely to DAOs.
  • Example II - go through list and add business validation messages to the result
    • service layer, no doubt
  • Example III - go through the list and prepare UI messages based on the validation errors
    • presentation layer

Side note:

  • MVC is a different thing from three-layered architecture.
  • M model spans across all three layers. Presentation layer includes both V views and C controllers.
Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
  • On your first point, are you saying a service should never call another service? – Usman Mutawakil Sep 29 '14 at 15:38
  • That is what we are following in our applications. If we need to reuse some logic, we usually abstract that into another component. – Pavel Horal Sep 29 '14 at 18:27
  • I'm not sure I would use the word "never" though. Login/UserVerificationService for example. You might need to call this before allowing any CRUD operation in any service to take place while at the same time it has its own associated controller and view for a dedicated login page. The same applies to an email service. I think these things are beyond helper or utility classes and are most definitely required by other services. – Usman Mutawakil Sep 30 '14 at 03:34
  • "Never" might be too strong. But we have not yet break this rule. You have used two examples I would implement differently. SECURITY is a cross cut concern handled by AOP or security components (even thou login itself might be handled by a business service). – Pavel Horal Sep 30 '14 at 06:39
  • For e-mail we use `NotificationDispatcher` component. You might say we are "cheating on our rules" because we just renamed `EmailService`. And you would not be far from truth. However we have just stay truth to our rules and this component is only called from `*Service` classes. So higher level components need to go through business service. – Pavel Horal Sep 30 '14 at 06:41
  • The main reason behind this rule is that at some point we have decided we don't want circular dependencies. And this rule makes it easier. If you don't have such rule you are tempted to call services between them on every step. And not only them - we use this rule on every layer (calling dao from dao is prohibited, etc.). Also when we started following this rule, somehow we were forced to make better encapsulated components... well it makes you think twice about the design, which is good :). – Pavel Horal Sep 30 '14 at 06:46