Symfony 4: How to move fat logic to Services

Symfony 4 logo

One of my recurrent concerns while building MVC applications is to have no extensive logic in the controllers.

That’s why is common to say “Skinny Controllers, Fat Models”.

But is unclear what to do when the model becomes too fat. This can easily happen while mixing structure and business logic. Your models could become too big and complex.

One of the possible solutions is extracting code from the models to services. You’ll get cleaner, and better organized code. Code easier to maintain.

This way you’ll say instead, “Skinny Models, Skinny Controllers, Fat Services”.

Is a way of managing logic, so that you achieve Don’t Repeat Yourself (DRY), avoiding to have the same logic in different places, and not having controllers and/or models taking too much responsibility.

Another advantage is that, if you have logic in simple POPO (Plain Old PHP Objects), the unit tests became simpler to make.

In this article I’m going to share with you, how to achieve that with Symfony 4, step by step. Stay with me.

First Step: Create the Controller

This is what I typically have in the controllers:

  • Handle a request and create a response;
  • Create and handle forms;
  • Bind parameters;
  • Get and set session objects;
  • Redirects.

Everything else I move to different places.

Now you can create the controller, using the following command:

bin/console make:controller

After you complete, this will create a new folder, named Controller, for the controllers, with the naming you choose.

Second Step: Create the Entity and Repositories

The entities are the representation and structure of objects that you will need.

Additionally repositories are simple PHP classes whose only job is to help fetch entities of a certain class. They are a way of isolate, reuse and test the queries to the database.

Is a good practice to create a custom repository class for the entity. Methods that have query logic, can then be stored in this class.

You can create an entity using the following command. The respective repository will also be created.

bin/console make:entity

And that’s done.

Third Step: Create the Service

For the Services I simply create a folder named “Service” inside of src/ folder. I always use singular in naming (excepting variables to be used in loops).

In here I create folder categories, according to groups that I can have. These could be related to models, process, or type of services. The namespaces help also to organize them in a modular way. Some examples:

Service/Facebook/PostToFacebook.php
Service/Search/SearchByText.php
Service/Post/SavePost.php

And that’s it! Services come to the rescue.

Note: One practice common in Ruby Object Services is to have a specific method named “call”. For example, you could define an interface that assures this principle.


Some best practices

I’d also recommend you to apply some software development best practices, such as Single Responsibility Principle (SRP) and Test-Driven Development (TDD). The first one refers to the first two letters of SOLID. And you could also combine this with Guard Clauses.

Guard Clauses are a way of writing code that makes your flow of logic cleaner, because you now that certain conditions are met.

I tend to add Guard Clauses inside of services. This way I centralize all the data verifications in one place.

And to achieve the SRP, your services should have only one responsibility. One single purpose, and only one reason to change. This will also allow to easier do unit tests and identify purpose.

What do you think?

I think this way you will have your source code more organized, and will also allow you to reuse it.

Imagine you have commands that will need to create and manage data. These could use the services, also as controllers can.

What do you think? Do you like this way of structuring your source code? What kind of strategies do you use to avoid bloated code? Tell me in comments!

This article belongs to a series of posts about Symfony 4: