The biggest complaint people have about object-oriented design is where they put all the “glue code” that ties together a bunch of objects and makes them work well. For many, this place is the controller, but as I’ve covered before, most of this logic belongs in the model. In fact, the model should be where all business logic resides. And yet, it can still seem difficult to figure out precisely how to manage all of these behaviors in one place and still follow the best practices of object-oriented design and development.

But there is a way to accomplish this that won’t make you crazy. It’s accomplished by making use of “services” – bits of code that act as the “glue code” for all the different objects that have to operate.

What is a service?

A service is a discrete collection of code that serves a particular purpose. For example, creating a user would be its own service, and logging a user in would be another service. Each service is an object in and of itself. Each service has a single job (remember the Single Responsibility Principle), and usually has only one or two public methods. Each service receives any number of objects when instantiated, and uses those objects to accomplish its task.

How do you create a service?

By definition, services should be discrete bits of code that interact with your other objects. Services aren’t places for HTML or display logic; they are solely places for performing a particular task. Because services are almost always unique from one another, it’s rare to see a service that extends an abstract service or implements in interface. Instead, services are “one off” objects.

To write a service, consider carefully what specific job you want your service to perform. It’s easy to pass too many responsibilities to a single service, thus defeating the purpose of a service altogether. For example, in account creation (the earlier example), you might also make the mistake of including the sending of the welcome email. But this is its own discrete task, and should really be its own service.

Breaking down your services into really tiny pieces is the key to creating a service you won’t hate later on. It will make testing easier, and make maintenance possible. Otherwise, you end up with a big ball of code that is no better off than what you’re trying to avoid.

What belongs in a service, and what doesn’t.

In my services, I include the instantiation of entities, their persistence, the sending of emails, the operation of cron jobs, and other typical application behaviors. These are the components that make up a good service. But the list of what I exclude is longer, and more important overall.

I exclude anything from my service that relates directly to the fact that this is a web application. For example, I never access the session from a service, or expect there to be a Request object or POST/GET data. I may incorporate a user into my services, but I never expect that user to be stored in a session; the user should simply be provided to me.

In other words, my services would be just as happy in an API or a command line environment as they would be on the web. By the time data gets to the service, it’s already been parsed out of the “web environment” and turned into something my service can digest. That makes my services flexible for use in other ways.

Interaction with the environment is the job of your controller. Your controller can care about the session and the request. It can grab the POST/GET data, and check the session for the user’s status. The controller can be custom-designed to fit the particular environment, but the service should be agnostic.

Services make testing easier.

I use services because they make testing that much easier.

Services serve as a mediator between other objects (see the Mediator Pattern). These other objects can be fully tested, and the service (with its interactions) can also be fully tested. In addition, controllers can be tested. Testing your controllers lets you create even finer grain testing suites, or even create mock objects to test how controllers respond.

Services that are well-written are the key to having well-designed applications. If you’ve written off services as big bundles of messy code, reconsider writing clean, clear services and see if they can improve your application.

Frustrated with your company’s development practices? You don't have to be! No matter what the issues are, they can be fixed. You can begin to shed light on these issues with my handy checklist. Plus, I'll help you with strategies to approach the issues at the organization level and "punch above your weight."

Great! We'll be updating you soon on best practices for your team!

Brandon Savage is the author of Mastering Object Oriented PHP and Practical Design Patterns in PHP