Skip to main content

Laravel Project Architecture: The Missing Guide

At my job, we've been doing a lot of learning and development using Taylor Otwell's Laravel 4 PHP framework.  As we've become more familiar with it, we've had to come up with better ways to structure our projects outside of what the documentation indicates and the default distribution that is provided.

If you've been working with Laravel 4 for any amount of time or come with experience from another framework and are just getting started, you've probably noticed that there are a lot of different ways to cut up your projects.


Choice is nice, but sometimes it can be paralysing or misleading.

Concrete Advice

This post is done in such a way that you can just skim the headings, but if you want a detailed explanation in each section, feel free to read in where necessary.

While I can't say the entirety of my advice is in practice throughout the community, I can say that we are starting to use it, and to very good effect at my job.  Especially considering that our apps tend to need both web and API front ends, good architecture is a very high priority.

Use Namespaces

...properly!  I've seen a lot of packages out there that classmap a file with a namespace declaration in it and yes while that's technically viable and nothing will blow up, it's cluttered.  The whole idea behind PSR namespace loading is that the files are easy to find and cohesive.  Resorting to a "just find it all here under this esoteric name I've given it" approach makes your project more difficult for others to get up to speed with.

How do you namespace properly?  At the root of your project (as in: adjacent to your .git directory), have a directory called "src".  Inside of there you might have: "YourOrganization/YourProject", followed by all the necessary namespaces for the guts of your application.  Some of which I will be clearly mentioning later in this post.

This is merely laying the groundwork for what's to come, but before we move on, there are two more suggestions I have which some people may disagree with:
  • Never repeat namespace names in classes.  So nothing like "YourOrganization\YourProject\Controller\PostController".  If you need a different name for the class, alias it in the file where you're using it, where the meaning is really implied.
  • Never pluralize your namespaces.  It's not necessary and will make sense once you see a complete class name.

Don't use Facades in Namespaced Code

It may seem tempting, but every time you do it, you're turning down an opportunity to make use of the IoC and reap the benefits of a clearer architecture.  Instead, define a constructor on your class and list the dependencies there.

Namespace Your Models

It's just going to be more organized that way and you'll be less inclined to abuse them.

Only Extend Model

I mean this in two ways:

First, don't extend "Eloquent", extend the fully namespaced eloquent class "Model" itself.

Second, if you see a package claim along the lines of "oh yes, we will give you slugs, just extend our sluggable base class and away you go!", immediately put on the brakes.  Go to that project's github page and open an issue titled: "Use Traits Instead" with the body text "Please see title.", click submit.  The response will usually be "oh most people don't have PHP 5.4", at which point you can remind them what year it is and that PHP 5.3 is EoL.

Write Thin Models

...with no external dependencies.  That means nothing like:

    public function getUrlAttribute() {
        return $this->urlGeneratorService->generateUrl($this->attributes['name']);
    }

If you've been paying attention, you'll realize doing this in your model would force it to depend on an external service - and that implies a context, which is undesirable.  If you write methods like this in your models, you will have architecture problems down the line.

If there's some kind of transformation you need to perform on model data, it's entirely likely that it is specific to a certain circumstance that model may not always be finding itself in.  That circumstance should never be coupled to the model itself, ever.

What can you write in your models?  Basically anything that leverages the model itself, its relations, internal PHP methods, scopes or something from the "Illuminate/Database/Eloquent" namespace.

Create Repositories

Repository classes represent the different graphs you'll be requesting from the database.  A repository might look as simple as an Eloquent model getter (and seem redundant), but creating this layer will allow you to keep your classes from becoming a dizzy mess of queries.

Everything else is a Service

Where something isn't a repository to fetch models from the database, you can create a service.  Whether it's a utility to build static CDN URLs, or a social library that you've created.  Most everything else in Laravel can be defined and injected as a service to be called.  These can either call each other or be mixed and matched at the right level as needed.

Namespace your Controllers

Controllers will depend on services and/or repositories, and services are injected. Best to get them in the structure.  Here's where I'd hope to find a controller in your project:

  • your-project/
    • app/
    • src/
      • YourOrganization
        • YourProjectName
          • Controller
            • Api
              • User
              • Post
              • Social
            • Web
              • User
              • Post
              • Admin
          • Model
          • Repository
          • Service
    • public/
    • vendor/
Notice that I don't repeat names anywhere and everything is split up nicely based on roles.  I have clean names to use in my routes and nobody has to go digging to find specific functionality.  The separation of concerns in my project is clear based on this namespace structure alone.

Non-Namespaced Files

Migrations, views, routes, and configs are all the kinds of configuration-like framework files that don't need to be namespaced.  This is where the facades really come in handy to grant your static code access to the dependency injection container.

On Migrations

Don't use the enum type in Laravel 4 migrations currently as you'll lose the ability to alter the table later on down the line.  Also, when authoring migrations for a package, write them to exist in the package and not to be copied into the main project by a command.  This was something I got tricked into doing and it ended up being unoptimal when the time came for updates.

The More Theoretical

These next few are more to do with how your efforts on a general whole can be improved when working with Laravel 4.  I just think they're a little more abstract than "do this with this here and not there".

Don't Subclass Packages

If you see a particular package in the wild and think to yourself "hm, I just need to change this one method on this class...", I urge you to reconsider.  Either think about it from the angle of wrapping it with a service, or fork the project and turn the original package into an upstream source.  The amount of confusion that can arise just from having multiple service providers attaching to the same realm of functionality as well as any potential model issues will mire you.

Write Packages!

...and then open source them!  Release them to the community and help the community grow.  I have a co-worker who firmly believes that nothing is too small to be a package.  With the right designs in mind, I'd say he's right!

Here's how I like my packages to look:

  • your-package/
    • config/
    • migrations/
    • src/
      • YourOrganization/
        • YourPackageName/
          • {any namespaces needed}/
          • ServiceProvider.php
    • composer.json
    • readme.md
I'm a bit on the fence about putting migrations and config in "src" as I think it should always be the root of a namespaced loader.  If you have any thoughts on that, feel free to send me a comment!

Don't Fight It!

I was given some good advice very early on when I started working with Symfony 1.4 which was to never try and fight the framework.  Always work with the grain, go with the flow.  Given the title of my blog, you can correctly conclude that I tend to challenge convention wherever possible.  But that doesn't mean I'm immune to well thought explanations.  In this, frameworks shine.

Generally speaking, if a framework is popular and has a strong community, it stands to reason that many eyes have gone over the tools and given them a general "ok" in a broader scale design analysis.  I think the irresistable force paradox merits mention here.

Use the Local Development Server

Don't let the vagaries of any environment make you think that Laravel should be expected to behave differently in production.  See my previous point.  I think you'll find some parallels between this practice and writing tests for your code as well.  If you can't download your project, install dependencies and then run artisan, you have a problem.

Be Pragmatic

If you haven't read the Pragmatic Programmer and are not familiar with one of Symfony (1.x or 2.x) or Ruby on Rails, do yourself a favour and read the book.  I think a lot of the advice in that book urges us to think more about our work with a longer term focus than is normally seen.  What's important is not making decisions out of convenience and being aware of impatient duplication, orthogonality and the broken windows theory.  All of these topics are explored to good effect in the book and help us learn a lot about ourselves as developers.

Be Still

I have one of my own to add which is to always try and write as little code as possible.  Code that conducts excessive checks or produces more data structures than is necessary always has to be balanced somewhere else down the line.  All of my advice here in a lot of ways is based on the desire to keep things DRY and simple.


Alright, that's it for now!  New guidelines can come along in another post if I come up with a decent enough list.
I've been tinkering away at this post for a bit and I think what's in here is good enough to share.  It's definitely the kind of resource I had hoped for when getting started with Laravel.  Please feel free to post a comment with your thoughts or improvements.

Keep in mind that what I've listed here is actually a combination of things I've discovered both on my own as well as through the community.

Comments

  1. This article was written by a real thinking writer. I agree many of the with the solid points made by the writer. I’ll be back. تحويل word الى pdf

    ReplyDelete
  2. Your blog is in a convincing manner, thanks for sharing such an information with lots of your effort and time
    ruby on rails training India
    ruby on rails training hyderabad

    ReplyDelete
  3. I was nervous when I Clicked on this on this blog I felt what im looking for this blogs haves but thank you to the author who has everything in this blog for what im looking for. This blog writer has the best knowledge about laravel.

    If you're looking for Laravel Web Development Services in India so, you can visit our CodeKing Solutions for the best offers.

    ReplyDelete

Post a Comment

Popular posts from this blog

Building .NET Core Nuget Packages

My last blog post was on building and publishing npm packages, specifically for typescript projects. In my opinion, packages are an important fundamental unit in software development. They sit at the confluence of technical competence and collaboration, and represent something the software community should be proud of. Most of the time, you're going to be creating software packages once you're comfortable with some essential pillars: Coding Project structure Software architecture Building Delivery Community Licensing After I got my npm package up and running, my next task was to do the same thing with my C# libraries. Similar to protoculture, I have made a library called praxis .  This is done by leveraging the services and tooling known in the .NET ecosystem as nuget. In this case, praxis abstracts many of the concepts and functionality I require when producing server projects. It builds on top of ASP.NET Core, so in that sense you can almost think of it as ...

Laravel is Good, Facades Aren't

I've been working on some Laravel 4 based packages lately which inevitably results in me also looking at other packages. I've noticed sometimes that people use facades at times that give me pause. The most troubling being from inside their model classes. A quick google turned up this video which assures people "there's still an instance behind everything, we're fine" .  Everything mentioned in the video is true except that there is a very glaring omission. Scope What usually goes out the door at the start of a long series of mishaps in software design is scope . When the desire to obtain a solution is stronger than the desire to consider the implications of a firm approach, mistakes are sure to follow.  Sacrifices like this are made due to the assumption of a high cost to developing carefully. What really is happening however is a false dilemma , being responded to with a convenience decision . It's very easy to write model code like ...