I wonder what is your approach on following subject:
let’s say we have MVC structure with entities, repositories, controllers and service layer.
My approach to use above was pretty clear for me:
- controller uses services
- services perform manipulations to data when
needed, - services use repository classess to save and update data
from db - controller gets services output (when needed) and pushes it
to view
However, yesterday I stumbled upon solution popular in Symfony2
world, in which
- controller has access to entityManager/sessionFactory
- controller uses services to perform manipulations to data
- controller uses entityManager/sessionFactory and repositories directly to perform database reads, saves and handle transactions.
At first I disagreed with second approach, I know it breaks some clean architecture principles, but… it is shown in a lot of official tutorials on Symfony2
website. So I thought it would be better to ask.
In other words:
is it ok to make controller directly responsible for saving and retrieving data as well as handling transactions? What are ups and downs?
There’s a risk that the second approach leads to a Fat Controller, which is often considered an antipattern. It’s effectively a violation of “clean architecture” (as by Uncle Bob Martin) since your controller is now not only a “delivery mechanism” but also deals with applicative transactions/use cases, orchestrating calls to the repositories and services and making decisions based on current state of the application.
Drawbacks are on readability, bug proneness, maintainability and testability since UI logic is now intertwined with applicative logic. It can also favor code duplication (in contrast, code in a service can be reused by multiple controllers) and if you choose to add another UI layer (say, mobile) to your architecture, the use cases will have to be copied there. Same if you want to add a UI-less HTTP API.
The main advantage is simplicity. With a small CRUD application where each HTTP request results in the same basic steps (load entity, update/delete/display it, [save entity]), you don’t necessarily need this layer of indirection. Applicative logic can even be factored out to a base controller class with template methods.
2