Laravel: Modularizing Little Pieces of Logic Through Local Composer Packages

Gemma BlackGemma Black
2 min read

“Premature optimisation is the root of all evil”.

But sometimes you know upfront that you can create a clean module, separate from the rest of the Laravel app. And I like Kelly Sutton’s mantra – optimize for deletability.

Why?

“By working with code, we see that modularity and deletability are closely related. Properly modularized code is easy to delete”. - Kelly Sutton

If a feature is spread across multiple files throughout different parts of the code base, when it comes to updating and maintaining the code, it means looking around everywhere to do so. It’s inefficient. And if you plan on having the code for a long time, chances are, it will be changed.

So a simple solution to keep the new feature separate and modularised, is to create a new autoload configuration in the composer.json file eg.

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/",
            "YourLib\\": "lib/"
        }
    },

And voila!

Add your your new feature there:

touch lib/Abc/Example.php
<?php

namespace YourLib\Abc;

class Example
{
    public function greet(string $name): string
    {
        return "Hello, $name!";
    }
}

A step further

You can treat it like its own Composer package.

mkdir -p lib/composer/example
cd lib/composer/example

You can define its dependencies:

{
    "name": "your-namespace/example",
    "description": "An example package",
    "type": "library",
    "autoload": {
        "psr-4": {
            "YourNamespace\\Example\\": "src/"
        }
    },
    "require": {
        "php": "^8.2"
    }
}

Then you can add it to your main composer.json file as a local repository:

"repositories": [
    {
        "type": "path",
        "url": "lib/composer/*"
    }
]

And finally reference it as if it was a separate package.

"require": {
    "your-namespace/example": "*@dev"
}

You’ll need to run composer update otherwise your code won’t find it.

And when you’re done, you can put it into its own repository and install it like any other compose package.

Downsides of creating a local package

So going to all this effort to create a local package that you don’t publish won’t prevent you from hoisting in application code into your module, at which, you’re now polluting the separated module.

If you’re not going to truly separate the package out at some point, just keeping it as an autoload path is more than sufficient. But it suffers the same problem, that you need discipline to not use your main application code or other modules within it.

0
Subscribe to my newsletter

Read articles from Gemma Black directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gemma Black
Gemma Black

I'm a Senior Software Engineer. With 10+ years working within tech teams, and 20+ years working with code, I develop across the stack, assisting with application design, maintenance, deployment and DevOps within the AWS Cloud.