So we have a PHP+Zend Framework+Doctrine 1.2 application that has the following structure:
Controller -> Action -> Service -> Model
Not all models are interacted with through a service. Our current opinion is that a controller could use a model directly and a service could possibly use other services.
My question is: If you have a piece of business logic, what guidelines do you use to determine if the code that implements that logic should be in the Model, Controller, or Service layer? I am specifically interested in distinctions between the Controller and Service layer.
Here are the guidelines that our development team have come up with, but I am very interested in any feedback/input about them:
- Services and controllers contain
logic for tying various components
together to accomplish a task.
This logic might not be in the
model to avoid dependencies and make
the model more reusable.
This
logic might also not be in the model
because we think the model should
avoid consuming things up the
application stack to avoid
unnecessary dependencies (ex: the
model would not consume a service or
controller). - Use a service rather than a controller when the code may be used
by multiple modules or controllers. - A model should contain as much logic as possible but avoid
referencing application-specific
functionality. Usually a model
contains at least validation logic. - For any piece of functionality consider placing it in the model
first. If there is a compelling
reason not to, consider a service
(also consider the overhead and
purpose for maintain a new service).
If a service is not desired and the
code won’t be reused in this
application use a controller.
In your context, I’d see models and controllers as both being parts of the business logic; the models defining “what” things are, the controllers defining “how” they are accessed.
Services sit on top, potentially exposing the business logic to anything outside of the business logic layer. I agree with you that a service might encapsulate more than one specific component (or model to be more precise).
Yes, I also agree with your statement on avoiding dependancies, etc. the model should not depend on anything excpet other closely related models (Common Closure Principle).
In addition, if the logic is specific to a model – then that’s where it should go; if the logic is more generic it should be placed at the approriate level – possibly a controller or a common internal utility.
I agree. In terms of grainularity I would see services being at a higher level of abstraction – you’re more likely to expose a service to an external system than your “internal” controllers.
It should only contain logic that’s appropriate to be there, otherwise I agree. Validation – you could abtract that out; the model should definately contain the rules that validation uses but not necessarily the validation itself. I’ve seen both styles used and I don’t think there’s no wrong or right answer as long as you’re consistent.
it depends what that “functinality” is, if it’s specific to a model then it probably belongs in the model; if it’s common to more than one model then it either belongs in a controller or a common utility class within the business logic.
As I started writing all of this I wanted to put a stake in the ground around the definition of the terms you’ve used; I thought I’d include it anyway – so correct me if I’m wrong 🙂 And as you can see, I’m not clear on what you mean by an “action” in your context.