In my professional live I mostly work with enterprise web applications which are quite demanding for big layer of business logic (that’s another article I guess) and decoupling of application layers. During this year I invested quite a lot for a search of a good ways to architecture a big application and make it simply good. Quite a while ago Matthew Weier O’Phinney introduced service layer in one of his great talks about models, since then service layer become one of the key architectural component one my applications. Here I’m going to show a few examples and use cases where it’s very useful.
“Old-style” interaction with data
I’ve used it for different projects, but one of the best examples of how great this concept is SaaS or any other users-based application. Some years ago I used to have code which worked like this (let’s say this is controller action):
public function userInfo() { $userDao = new Users(); $user = $userDao->getUser($_SESSION['id']); }
And then in my database access class Users I just execute sql query with a given user id. However, after some thinking I looked at it again and though – why does controller need to know the userId? I mean, of course it’s a job of controller to process requests and control application flow, but logically – if an action is named userInfo and we got to the point where we need the user info (hence the user is authenticated and validated) why do we need to pass user id? It’s clear that some part of code already knows it.
One more case: if a site is a e-commerce it’s clear that user has only access to his orders, addresses information etc. but in a basic MVC you either fetch by Id and then check if it’s in fact user’s order or create a method like in a first example. Again, not very clear and not easy to maintain. There are more problems too: by passing user id and id of a record you assume that controller knows that this is a key to get the information. But it’s wrong – business layer knows that user has orders; controller only knows that there is such a thing like orders and it can be retrieved by id. That’s it.
Service layer
For such things I use service layer: it has user info injected from bootstrap (or directly to a constructor) and operates with data only accessible to the user. So previous method becomes to:
public function userInfo() { $service = new UsersService(); $user = $service->getUser(); }
of course you would have a separate method to fetch other users data, but for sake of simplicity let’s just say that this method returns some private info (like address for example). Here my action is completely unaware of what user id actually is – it expects a user object, it gets it. Very simple, very clean and very easy to maintain.
Getting back to the e-commerce example, action for a view order would look like this:
public function viewOrder() { $service = new OrdersService(); $order = $service->getOrder($_GET['id']); }
Again – this controller action doesn’t care what user id is in current session, it just gets an order by its id. If this action returns false (or throws exception with error type) then it means order cannot be retrieved, or in other words – service cannot return id by given id. It can be permissions problem, it can be something else – but controller is completely freed from checking all this unnecessary things.
Practical usage
As you might have noticed, service layer is intermediate layer between models and controllers – in the same way as you would use Flickr or Youtube API to work with remote data, you use service layer API to work with application resources. All the business logic resides in service layer, where also using other service layer, models are retrieved, changed, saved, returned etc. Controller has zero lines which contain a word Doctrine (or any other database layer class). None.

Another advantage of using a service layer – it’s a good place to merge information sources. I think it’s more like a style of mine, but I tend to have models very clean – only with logic on that model. This is mainly because I usually use Doctrine models and I don’t want to put anything in them. For example just yesterday Adam from jazzslider.org posted a very good article about using Acl with models by creating custom listeners. With all respect, even though it works really great, I don’t think it’s a clean approach – I see models and permissions control as separate layers.
Furthermore, having this layer makes replacing database layer (or even models layer) a little bit easier – because all the other code communicates with data using given API (from service layer) so as long as results returned are the same, they don’t care how they are actually retrieved (for example they can come from cache, text file or created on-the-fly). But if you would have Zend_Db_Select calls all other the place, migrating to Doctrine’s DQL can be a pain. From my personal experience, I successfully migrated my own ORM code with about 50 models to Doctrine in about 4 days without changing a line in controllers (also because of tests I had).
To be honest, I’ve only tried various service layer implementations with Zend Framework. Even a default autoloader has a resource namespace Service_ with services folder inside application, so I didn’t need to do any hacking to get it working. Nevertheless, this pattern doesn’t require any specific framework futures, but if a framework has good dependency injector (like this one from Symfony) it can make things even cleaner.
Conclusion
I don’t know if I have convinced you to look at this pattern, but I definitely recommend looking at it. Especially when your application gets quite big and you need some sort of functionality to work with all these models (on average, I used service layer with applications having roughly 80 models). Nevertheless, there are tons of different ways to do this, so I definitely recommend reading a book by M. Fowler called “Patterns of Enterprise Application Architecture (P of EAA)”. One of the best sources for enterprise applications I’ve read so far.
* Image copyright: http://martinfowler.com/eaaCatalog/serviceLayer.html and http://www.tutorialspoint.com/images/soa-additional-service-layer.jpg







