r2luna / brain
Brain: A process-driven architecture alternative for your Laravel Application.
Installs: 4 789
Dependents: 1
Suggesters: 0
Security: 0
Stars: 46
Watchers: 5
Forks: 5
Open Issues: 1
pkg:composer/r2luna/brain
Requires
- php: ^8.3.0
- laravel/framework: ^11.37|^12.0
- phpdocumentor/reflection: ^6.1
Requires (Dev)
- captainhook/captainhook: ^5.25
- captainhook/hook-installer: ^1.0
- laradumps/laradumps: ^4.0
- laravel/pint: ^1.21
- mockery/mockery: ^1.6.12
- orchestra/testbench: ^9.0|^10.0
- peckphp/peck: ^0.1.2
- pestphp/pest: ^3.7
- pestphp/pest-plugin-type-coverage: ^3.3
- phpstan/phpstan: ^2.1
- rector/rector: ^2.0
README
Brain is an elegant Laravel Package that helps you organize your Laravel application using Domain-Driven Design principles through a simple command-line interface.
Features
- 🎯 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
Gains
- ♻️ 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.
Installation
You can install the package via composer:
composer require r2luna/brain
Usage
Creating a Process
php artisan make:process ... follow prompt name: CreateUserProcess domain: Users
This 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.
Creating a Task
php artisan make:task ... follow prompt name: SendWelcomeEmailTask domain: Users
This will create a new task class in app/Brain/Users/Tasks/SendWelcomeEmailTask.php
Queuable Tasks
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; } }
Delay Queueable Tasks
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); } ... }
Conditional Run Tasks in a Process
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; } ... }
Validating Task Properties
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.
Task helper methods
toArray(): Returns the task properties as an array. Ex.['user' => $this->user]
Cancel the Process
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.
Creating a Query
php artisan make:query ... follow prompt name: GetUserByEmailQuery domain: Users model: User
This will create a new query class in app/Brain/Users/Queries/GetUserByEmailQuery.php
Example Usage
// Using a Query $user = GetUserByEmailQuery::run('john@example.com'); // 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' => 'john@example.com' ]); // Using a Task without a process SendWelcomeEmailTask::dispatch([ 'user' => $user ]);
Architecture
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.
Logging
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.
Configuration
To enable logging, set the BRAIN_LOG_ENABLED environment variable or update the config file:
# .env
BRAIN_LOG_ENABLED=true
Or publish and modify the configuration file:
php artisan vendor:publish --provider="Brain\BrainServiceProvider"
Then update config/brain.php:
'log' => env('BRAIN_LOG_ENABLED', true),
Events
Brain dispatches events throughout the lifecycle of processes and tasks. These events can be used for logging, monitoring, or triggering additional actions.
Process Events
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
Task Events
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
Custom Event Listeners
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, ], ];
Testing
composer test
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security-related issues, please email rafael@lunardelli.me instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.