aldemeery/onion
Aldemeery Onion is a Laravel package for adding “onion” (layered) architecture patterns to your app, helping you organize code into clear domain, application, and infrastructure layers with cleaner boundaries, structure, and maintainability.
Installation
composer require aldemeery/onion
Add the service provider to config/app.php:
'providers' => [
Aldemeery\Onion\OnionServiceProvider::class,
],
Basic Layer Definition
Define layers in config/onion.php:
'layers' => [
'api' => [
'middleware' => ['api', 'auth:sanctum'],
'routes' => ['api.php'],
],
'web' => [
'middleware' => ['web'],
'routes' => ['web.php'],
],
],
First Use Case Register a layer-aware service in a provider:
$this->app->singleton('MyLayerService', function ($app) {
return new MyLayerService($app['onion']->layer('api'));
});
config/onion.php (layer definitions)Onion::layer('layer-name') (access layers)Aldemeery\Onion\OnionServiceProvider (boot logic)Wrap middleware to conditionally apply based on layers:
class LayerAwareMiddleware {
public function __construct(protected Onion $onion) {}
public function handle($request, Closure $next) {
if ($this->onion->isActive('api')) {
return $next($request);
}
return response('Not allowed', 403);
}
}
Use layers to group routes with shared middleware:
Route::group(['layer' => 'api'], function () {
Route::get('/users', [UserController::class, 'index']);
});
Resolve services scoped to a layer:
class UserController {
public function __construct(
public UserService $service,
public Onion $onion
) {
$this->service = $this->onion->layer('api')->make(UserService::class);
}
}
Override config per layer:
$onion = app('onion');
$apiConfig = $onion->layer('api')->config('services.stripe.key');
config/onion.php.$this->app->when('onion.api')->needs('MyService')->give(MyService::class);
Onion::layer('name')->make() to resolve layer-scoped dependencies.->needs() carefully to avoid conflicts.Onion::activeLayers() to debug which layers are currently active.dd($this->onion->activeLayers()) in middleware to inspect layer context.$onion->layer('api')->config('key');
api, web, cli, admin) to avoid ambiguity.Onion in tests to isolate layer-specific behavior:
$onionMock = Mockery::mock(Onion::class);
$onionMock->shouldReceive('layer')->andReturn($layerMock);
$this->app->instance(Onion::class, $onionMock);
Aldemeery\Onion\Layer to add custom layer logic.Onion::extend() to register custom layer resolvers:
Onion::extend('custom', function ($app) {
return new CustomLayer($app);
});
cli layer for Artisan commands:
'layers' => [
'cli' => [
'middleware' => ['standalone'],
'commands' => [\App\Console\Commands\*],
],
],
config/onion.php:
'default' => 'web',
How can I help you explore Laravel packages today?