Brain is an elegant Laravel Package that helps you organize your Laravel application using Domain-Driven Design principles through a simple command-line interface.
- 🎯 Domain-Driven Structure: Easily create new domains with proper architecture
- 🔄 Process Management: Generate process classes for complex business operations
- 🔍 Query Objects: Create dedicated query classes for database operations
- ⚡ Task Management: Generate task classes for background jobs and queue operations
- ♻️ Code Reusability: By using tasks, you can easily reuse code across different processes, reducing duplication and enhancing maintainability.
- đź§© Clear Domain Understanding: The structured approach provides a better understanding of each domain's processes, making it easier to manage and scale your application.
- đź”§ Improved Maintainability: With well-defined domains and processes, maintaining and updating your application becomes more straightforward and less error-prone.
You can install the package via composer:
composer require r2luna/brainphp artisan make:process
... follow prompt
name: CreateUserProcess
domain: UsersThis will create a new process class in app/Brain/Users/Processes/CreateUserProcess.php
Important
Note that every task running inside a process executes within a database transaction by default.
php artisan make:task
... follow prompt
name: SendWelcomeEmailTask
domain: UsersThis will create a new task class in app/Brain/Users/Tasks/SendWelcomeEmailTask.php
To send the task to the queue simply implements Laravel Contract ShouldQueue to the class
<?php
namespace App\Brain\User;
use Brain\Task;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeNotifications extends Task implements ShouldQueue
{
public function handle(): self
{
//
return $this;
}
}Brain Tasks has a protected function called runIn() that you can use to determine when do you want to run the job if you need to delay.
class SendWelcomeNotifications extends Task implements ShouldQueue
{
protected function runIn(): int|Carbon|null
{
return now()->addDays(2);
}
...
}You can use a protected function runIf() to conditionally run a task.
/**
* @property-read int $amount
*/
class SendWelcomeNotifications extends Task
{
protected function runIf(): bool
{
return $this->amount > 200;
}
...
}You can validate the properties passed to a task by defining a rules() method that returns an array of validation rules.
/**
* @property-read User $user
* @property string $message
*/
class SendWelcomeNotifications extends Task
{
public function rules(): array
{
return [
'user' => 'required',
'message' => 'required|string|max:255',
];
}
public function handle(): self
{
// ...
return $this;
}
...
}Rules will validate based on the payload that was passed to the task when it was dispatched, and will override the default validation based on the docblock @property-read annotations.
toArray(): Returns the task properties as an array. Ex.['user' => $this->user]
If you need, for any reason, cancel the process from inside a task. You can call cancelProcess() method to do it.
class AddRoles extends Task
{
public function handle(): self
{
if($anyReason) {
$this->cancelProcess();
}
return $this;
}
}Caution
This will not work if the task is setup to run in a queue.
php artisan make:query
... follow prompt
name: GetUserByEmailQuery
domain: Users
model: UserThis will create a new query class in app/Brain/Users/Queries/GetUserByEmailQuery.php
// Using a Query
$user = GetUserByEmailQuery::run('[email protected]');
// Setting up a Process
class CreateUserProcess extends Process
{
protected array $tasks = [
RegisterUserTask::class,
SendWelcomeEmailTask::class, // Async task
NotifyStaffTask::class, // Async task
SubProcess::class
];
}
// Using a Process
CreateUserProcess::dispatch([
'name' => 'John Doe',
'email' => '[email protected]'
]);
// Using a Task without a process
SendWelcomeEmailTask::dispatch([
'user' => $user
]);Brain helps you organize your code into three main concepts:
- Processes: Complex business operations that might involve multiple steps
- Queries: Database queries and data retrieval operations
- Tasks: Sync/Async operations that can be called as part of a process or not
Each concept is organized within its respective domain, promoting clean architecture and separation of concerns.
Brain provides built-in logging functionality to track the execution and outcomes of processes and tasks. By default, logging is disabled but can be enabled through configuration.
To enable logging, set the BRAIN_LOG_ENABLED environment variable or update the config file:
# .env
BRAIN_LOG_ENABLED=trueOr publish and modify the configuration file:
php artisan vendor:publish --provider="Brain\BrainServiceProvider"Then update config/brain.php:
'log' => env('BRAIN_LOG_ENABLED', true),Brain dispatches events throughout the lifecycle of processes and tasks. These events can be used for logging, monitoring, or triggering additional actions.
Brain\Processes\Events\Processing- Dispatched when a process starts executingBrain\Processes\Events\Processed- Dispatched when a process completes successfullyBrain\Processes\Events\Error- Dispatched when a process encounters an error
Each event contains:
process(string): The process class namerunProcessId(string): A unique ID for this executionpayload(array|object): The data passed to the processmeta(array): Additional metadata
Brain\Tasks\Events\Processing- Dispatched when a task starts executingBrain\Tasks\Events\Processed- Dispatched when a task completes successfullyBrain\Tasks\Events\Cancelled- Dispatched when a task is cancelled viacancelProcess()Brain\Tasks\Events\Skipped- Dispatched when a task is skipped (whenrunIf()returns false)Brain\Tasks\Events\Error- Dispatched when a task encounters an error
Each event contains:
task(string): The task class namepayload(array|object|null): The data passed to the taskprocess(string|null): The process class name (if running within a process)runProcessId(string|null): The process execution ID (if running within a process)meta(array): Additional metadata
You can create custom event listeners to handle these events:
// app/Providers/EventServiceProvider.php
use Brain\Processes\Events\Processed as ProcessProcessed;
use Brain\Tasks\Events\Error as TaskError;
protected $listen = [
ProcessProcessed::class => [
NotifyAdminOfProcessCompletion::class,
],
TaskError::class => [
LogTaskErrorToExternalService::class,
],
];composer testPlease see CONTRIBUTING for details.
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.