2 min read
Actions Pattern
Services / Actions
A service layer in your application centralises business logic in a single place, separate from controllers and models. This improves code organisation, maintainability, and testability by enforcing a single responsibility for each layer of your application.
- Shared Logic: When multiple controllers or parts of the application need to perform the same complex operation.
- Decoupling: To keep controllers lightweight by moving business logic elsewhere.
- Testability: Services are easier to mock and test independently.
Example: A User Service
Imagine you need a service to handle user-related operations, such as creating a new user and sending a welcome email.
namespace App\Services; use App\Models\User; use Illuminate\Support\Facades\Mail; class UserService { public function createUser(array $data): User { // Create a user $user = User::create($data); // Send a welcome email Mail::to($user->email)->send(new WelcomeEmail($user)); return $user; } }
Another popular approach to organising business logic is using action classes. Action classes are single-purpose classes designed to encapsulate a specific piece of functionality. They provide a lightweight way to organise logic into reusable, testable units.
When to Use Actions
- Single Responsibility: To encapsulate a single, well-defined task.
- Reusability: When an action might be called from multiple parts of the application.
- Simplifying Controllers: Like services, they keep controllers clean but at a more granular level.
namespace App\Actions; use App\Models\User; use Illuminate\Support\Facades\Mail; class CreateUserAction { public function execute(array $data): User { // Create a user $user = User::create($data); // Send a welcome email Mail::to($user->email)->send(new WelcomeEmail($user)); return $user; } }
You can then use the action class in your controller like this:
namespace App\Http\Controllers; use App\Actions\CreateUserAction; use Illuminate\Http\Request; class UserController extends Controller { public function __construct(private CreateUserAction $createUserAction) {} public function store(Request $request) { $data = $request->validate([ 'name' => 'required|string', 'email' => 'required|email|unique:users', 'password' => 'required|string|min:8', ]); $user = $this->createUserAction->execute($data); return response()->json($user, 201); } }