← Back to Module

Service vs Action Classes

Learn when to extract business logic and which pattern to use

The Evolution of Business Logic

As applications grow, controllers become bloated with business logic. The solution is to extract this logic into dedicated classes.

1️⃣ Bloated Controller

All logic in controller (65+ lines per method)

2️⃣ Service Class

Group related operations (UserService with 4 methods)

3️⃣ Action Classes

Single responsibility (CreateUserAction, one job)

Try Each Approach

Click each button to see the same user registration implemented in different ways

Processing registration...

Code Metrics

Execution Time
Controller Lines
Complexity
Lower is better
Maintainability
Higher is better
Methods

Architecture

Controller:
Service:
Action Classes:

✓ Registration Successful

User

ID:

Name:

Email:

Company

ID:

Name:

Owner:

Team

ID:

Name:

Drawbacks:

Best For:

Side-by-Side Comparison

Metric Bloated Controller Service Class Action Classes
Lines of Code
Files Count
Testability
Maintainability
Reusability
Complexity
Overall Rating

Code Examples

❌ Bloated Controller

class UserController extends Controller {
    public function store(Request $request) {
        // 65+ lines of validation, user creation,
        // company setup, team management,
        // email sending, API calls, etc.
        // All in one massive method!
    }
}

⚠️ Service Class

class UserService {
    public function createUser(array $data) { /* ... */ }
    public function sendWelcomeEmail(User $user) { /* ... */ }
    public function updateProfile(User $user, array $data) { /* ... */ }
    public function deleteAccount(User $user) { /* ... */ }
}

class UserController extends Controller {
    public function store(Request $request, UserService $userService) {
        $user = $userService->createUser($request->validated());
        $userService->sendWelcomeEmail($user);
        return response()->json($user);
    }
}

✅ Action Classes

// CreateUserAction.php
class CreateUserAction {
    public function execute(array $data): User {
        // Only user creation logic
    }
}

// SendWelcomeEmailAction.php  
class SendWelcomeEmailAction {
    public function execute(User $user): void {
        // Only email sending logic
    }
}

// Controller
class UserController extends Controller {
    public function store(
        Request $request,
        CreateUserAction $createUser,
        SendWelcomeEmailAction $sendEmail
    ) {
        $user = $createUser->execute($request->validated());
        $sendEmail->execute($user);
        return response()->json($user);
    }
}

When to Use Each Pattern?

Bloated Controller

Only for:

  • • Quick prototypes
  • • Throwaway code
  • • Very simple apps

Service Class

Good for:

  • • Medium apps
  • • Refactoring legacy code
  • • Simple related operations

Action Classes ⭐

Best for:

  • • Large applications
  • • Complex business logic
  • • Long-term projects
  • • Strict testing requirements