Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Job Queue Laravel Package

micro-module/job-queue

A lightweight job queue module for Laravel micro-modules: dispatch and delay background jobs, process them with configurable workers, and handle retries and failures through a simple API that drops into any modular Laravel application.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and inspect before wiring anything up.

composer require micro-module/job-queue

Because this package has zero stars and zero dependents, treat installation as an audit step, not a convenience step. Before writing a single line of application code:

# What does it actually register?
cat vendor/micro-module/job-queue/composer.json
ls vendor/micro-module/job-queue/src
cat vendor/micro-module/job-queue/README.md

Look for the service provider (usually src/JobQueueServiceProvider.php or similar) and confirm:

  • Whether it auto-discovers via extra.laravel.providers or requires manual registration in bootstrap/providers.php.
  • Whether it ships a config file (config/job-queue.php) you can publish with php artisan vendor:publish --provider="MicroModule\\JobQueue\\...ServiceProvider".
  • Whether it ships migrations (a jobs table, a failed_jobs table, or a per-module queue table). If it does, run php artisan migrate and check the schema before proceeding.
  • What Laravel versions are allowed in composer.json require — pin your install accordingly (^1.0 rather than *).

2. Confirm the queue backend.

Most lightweight job-queue packages are thin wrappers over Laravel's native queue system. Check config/queue.php and the package's published config for the default connection (database, redis, sync). If the package does not ship a connection, it is almost certainly delegating to whatever QUEUE_CONNECTION is set to — verify this in the source, because that determines whether you need a worker process at all.

3. First use case: dispatch one job and watch it run.

Start with the smallest possible loop so you can isolate whether failures come from the package or from your job code.

// app/Jobs/PingJob.php
namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class PingJob implements ShouldQueue
{
    use Queueable;

    public function handle(): void
    {
        logger('pong');
    }
}
// routes/web.php (temporary)
Route::get('/ping', function () {
    dispatch(new \App\Jobs\PingJob());
    return 'queued';
});

Then:

php artisan queue:work --once -v
tail -f storage/logs/laravel.log

If pong appears, the package's plumbing is sound and you can move on. If it does not, you have learned something important about the package's maturity before it touched production. This single smoke test is the highest-value ten minutes you will spend with a zero-dependency package.

Implementation Patterns

Wrap the package behind your own dispatcher. With no dependents and no ecosystem, you do not want package-specific calls scattered across fifty call sites. Put one thin adapter in front of it:

// app/Support/Queue.php
namespace App\Support;

class Queue
{
    public static function push(object $job, ?string $queue = null): void
    {
        dispatch($job)->onQueue($queue ?? config('job-queue.default_queue', 'default'));
    }
}

Now swapping the package for laravel/horizon plus native queues is a one-file change. This is the single most important pattern for an unproven dependency.

Per-module queue routing. The micro-module naming suggests the package's intent is isolating work by module. Model that explicitly so operators can scale workers independently:

// config/job-queue.php
return [
    'default_queue' => 'default',
    'modules' => [
        'billing'   => 'queue-billing',
        'reporting' => 'queue-reporting',
    ],
];
Queue::push(new GenerateInvoice($order), config('job-queue.modules.billing'));

Then run dedicated workers: php artisan queue:work --queue=queue-billing.

Use native chaining and batching where the package stays out of the way. If the package delegates to Bus, you get Bus::chain() and Bus::batch() for free. Verify by reading the source; if it implements its own dispatcher, check whether it preserves the PendingDispatch return type. Losing chaining is a real cost and worth knowing up front.

Bus::chain([
    new ChargeCustomer($order),
    new SendReceipt($order),
    new SyncToAccounting($order),
])->onQueue('queue-billing')->dispatch();

Idempotency is your responsibility, not the package's. Queue systems deliver at-least-once. Every job that touches money, email, or external APIs should guard on a unique key:

public function handle(): void
{
    $key = "invoice:{$this->orderId}";
    if (! Cache::add($key, true, now()->addDay())) {
        return; // already processed
    }
    // ...
}

Test with Queue::fake(). Because the package is a black box, assert on your dispatch layer, not on the package's internals:

Queue::fake();

$this->post('/orders', [...])->assertRedirect();

Queue::assertPushed(GenerateInvoice::class, fn ($job) => $job->order->id === $order->id);

Gotchas and Tips

Treat the zero-star status as a real operational risk, not a formality. Zero stars and zero dependents means: no community bug reports, no Stack Overflow answers, no third party who has hit the edge case you are about to hit. Mitigations, in order of value:

  1. Fork it. Vendor the source into packages/ and use a Composer path repository. You get full control and a readable diff history if upstream changes.
  2. Pin the version exactly ("micro-module/job-queue": "1.0.4") rather than using a caret constraint. With a single-maintainer package, a patch release can silently change behavior.
  3. Vet the last release date. The 2025-04-16 release is recent, which is a positive signal — it suggests the package is not abandoned — but recent does not mean stable. Check whether it was tagged from a passing CI run.

Config caching will bite you. After publishing config, any change to config/job-queue.php is invisible until php artisan config:clear. During local development while you are still figuring out the package, keep config:cache out of your workflow entirely.

Workers do not reload code. The classic Laravel queue trap, and it applies double here because you may not know what the package caches in memory at boot. After every deploy:

php artisan queue:restart

If you deploy without this, old workers keep running old code against new job class signatures and you get confusing unserialize() errors.

Serialization failures look like dispatch failures. If you pass an Eloquent model into a job and the model is deleted before the worker picks it up, SerializesModels silently resolves it to null — or throws a ModelNotFoundException depending on configuration. For anything with a delay, pass scalar IDs and re-fetch inside handle():

public function __construct(public int $orderId) {}

Check that failed_jobs exists. A package can dispatch jobs perfectly and still have no failure table, which means failures vanish. Verify:

php artisan queue:failed

An empty result could mean "no failures" or "failures are being dropped on the floor." Inspect the schema of your failed_jobs table directly before trusting that output.

Set retry_after lower than worker --timeout. This misconfiguration causes jobs to be picked up twice — once by a worker that is still processing, once by a retry — producing duplicate side effects that look random. If the package ships its own defaults, they may not match your config/queue.php. Reconcile them explicitly.

Add your own observability rather than expecting the package to have it. With no dependents and likely no Horizon integration, you need a way to see queue depth and age. A five-minute scheduled check is enough:

// app/Console/Commands/QueueHealth.php
$depth = DB::table('jobs')->count();
$oldest = DB::table('jobs')->min('available_at');

Log::info('queue', ['depth' => $depth, 'oldest_age_s' => $oldest ? time() - $oldest : null]);

Alert when oldest_age_s exceeds your SLA. Without this, a stalled worker is invisible until customers complain.

Identify extension points early. Since you will likely need to modify behavior, find — and note in your team wiki — the bindings you can rebind in your own service provider:

$this->app->extend(SomePackageClass::class, function ($instance, $app) {
    return new YourSubclass($app);
});

If the package's internals are private and final end to end, extension is impossible and forking becomes mandatory rather than optional. Discovering that during your first feature request is far more expensive than discovering it now.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment