The layers attack!

Ivelin Pavlov
4 min readMay 29, 2018

--

I’ve always liked the idea of Laravel Middlewares and how they add complicated logic in a system without overwriting everything. The power of Pipeline design pattern is endless, except a pipe is broken and should be thrown. Now seriously, you can use it for authentication, filtering, response manipulation and etc. If you’d like to protect single model you can use a gate object or policy as a more general solution. These are great ways to extend the basic MVC pattern and separate the logic. But there is more.. extend the model with observers or custom events, search with scopes, extend model attributes with presenters for views or transformers for JSON responses and so many more. It’s like way too many layers and I feel my stack is overflown.

For a small project, we can use a few and our code base will be simple. But for larger applications, if we don’t think of some new cool architecture we can maintain again clean and simple workspace knowing what community and the framework already provides with those existing layers. The only thing left for you is to add some specific domain knowledge. Here are the layers I use and recommend:

Services

The most important one. It should go last because here I put everything that cannot be written in another layer. Typically I even extract the logic from the controller to keep it responsible only for the user’s input and preparing the response. E.g. TrackItemservice that calls external service and checks if an item has been changed. I like that you can automatically bind it in the controller’s method:

public function track(App\Services\TrackItem $trackItem)

Or you can easily get it from the application’s service container with app method of resolve alias:

app(App\Services\TrackItem::class)

Repositories

All advanced queries that include multiple models and cannot be described with a few query builder elements or scopes. Keeping the queries isolated gives you the ability to reuse them and keep your services cleaner. I use simple php class to create them the same way I create services as they are easy to use.

Scopes

Extends model queries easily. You can add them directly to your model with scopeScheduled to filter only scheduled records or separate them in another file or use a global scope to apply them to all queries. You can include whereHas on some relation and chain them, which gives you the ability to write complex queries within a line. Here is an example of Item with one-to-many relation toSchedule that keeps the frequency of repeated action for the item:

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
trait ScheduleScope
{
public function scopeScheduled(Builder $query, Carbon $time)
{
return $query->where('frequency', 'minutely')
->orWhere('frequency', 'hourly')
->where('at', $time->minute)
->orWhere('frequency', 'daily')
->where('at', $time->format('H:i'))
->orWhere('frequency', 'monthly')
->where('at', $time->format('d H:i'))
->where('hour', $time->hour);
}
}
trait Item
{
public function scopeScheduled(Builder $query)
{
return $query->whereHas('schedules', function (Builder $query) {
return $query->scheduled(Carbon::now());
});
}
}

This way you use traits in the corresponded models and you can call Item::scheduled() to fetch all items that need to perform an action now.

FormRequests

It’s great that all validations and authorization can be handled there. Keep your controllers clean and readable.

Policies

Store authorization logic for a single model. If you have multiple roles, permissions or groups it can simplify your code a lot. If there is a case you cannot catch or you have to protect only a single action you can use Gates.

Middlewares

A must for every single project as they simplify each Request lifecycle. There are tons of good open source libraries in GitHub that you can use. I sometimes just scan through all and see that there is something I want in the project, but didn’t have time to implement.

Observers

It’s good for handling some logic between related modules. Instead of creating triggers in MYSQL you can build more powerful actions from your observers.

Events

If you add listeners to services they are great to separate some logic like sending a notification after a new user is registered. I don’t want to do it in the Observer, because sometimes I can create a user manually outside of the service and I’d want not to notify the user about that.

Presenters

If you use views this is a must use for me. It keeps them clear of php code and logic. I prefer Laravel Auto Presenter, but you can use laracasts/Presenter by Jeffrey Way.

Transformers

Control responses to your API separately and add include relations with ease. You can also use the built-in Laravel Resource layer. It gives you better control over what users get as a result.

Serializers

Sometimes you may need to control the way your API response is organized. If you follow a standard or you want to invent your own optimized case.

Here is a screenshot of how all features look together in a project’s directory structure:

Some layers are only used for a small number of entities or have few lined classes. This is OK, I think, our logic has a place to grow and scale fast. In the past, I had lots of problems that files got bigger and bigger and I had to refactor the code to decrease the size of each module or class.

I am not including some top features of Laravel like Mails, Notifications, Commands or Queues,.. that are more like features, but they all give you already thought through solution of how to organize your project’s architecture. It favors SOLID principles which are easy to test and extend with new custom domain logic. The only thing left is to express your wildest imagination.

Please respond if there is something you think I’ve missed or you disagree on something. I am eager to see what you think as these are my first steps in writing.

--

--

Ivelin Pavlov

Passionate Web Developer with strong PHP experience. Interested in Informatics, data science, ml, and good programming habits.