Claude
Skills
Sign in
Back

Action Pattern Conventions

Included with Lifetime
$97 forever

This skill should be used when the user asks about "Laravel action pattern", "action class naming", "how to structure actions", "React component patterns", "Node.js service structure", "framework-specific conventions", or discusses creating reusable, focused classes following action pattern conventions in Laravel, Symfony, React, Vue, or Node.js projects.

Design

What this skill does


# Action Pattern Conventions

## Purpose

This skill provides language and framework-specific guidance for implementing the action pattern across different technology stacks. It explains conventions for creating focused, reusable action classes, components, services, and modules that encapsulate specific business operations.

## When to Use

Use this skill when refactoring code into actions, understanding how to structure operations for a specific framework, or ensuring extracted code follows project conventions. It covers Laravel, Symfony, React, Vue, and Node.js.

## Universal Action Pattern

The action pattern applies universally across frameworks:

**Core concept:** One action = one operation with a clear entry point

**Characteristics:**
- **Single responsibility**: One reason to exist
- **Clear interface**: Single entry method (`handle()`, `execute()`, call method)
- **Dependency injection**: Dependencies injected, not global
- **Reusability**: Can be called from multiple contexts (controllers, jobs, CLI, API, webhooks)
- **Testability**: Testable in isolation
- **Naming**: Describes the operation clearly

**Pattern:**
```
public function handle($input) {
    // 1. Validate/prepare input
    // 2. Execute operation (business logic)
    // 3. Return result or side effect
}
```

## Laravel Action Pattern

### File Structure

```
app/
├── Actions/
│   ├── Users/
│   │   ├── CreateUserAction.php
│   │   ├── UpdateUserAction.php
│   │   └── DeleteUserAction.php
│   ├── Orders/
│   │   ├── CreateOrderAction.php
│   │   └── ProcessPaymentAction.php
│   └── Notifications/
│       ├── SendWelcomeEmailAction.php
│       └── SendOrderConfirmationAction.php
```

### Basic Action Class

```php
<?php

namespace App\Actions\Users;

final readonly class CreateUserAction {
    public function __construct(private UserRepository $users) {}

    public function handle(array $data): User {
        // Validate (optional, can use Form Request instead)
        $validated = $this->validate($data);

        // Create user
        $user = $this->users->create($validated);

        // Side effects (notifications, etc.)
        // Only if tightly coupled to creation
        // Otherwise use jobs or separate actions

        return $user;
    }

    private function validate(array $data): array {
        // Custom validation if needed
        return $data;
    }
}
```

### Usage in Controllers

```php
class UserController extends Controller {
    public function store(CreateUserRequest $request, CreateUserAction $createUser) {
        // Constructor injection of action
        $user = $createUser->handle($request->validated());

        return response()->json(['user' => $user], 201);
    }
}
```

### Naming Conventions

**Action class names:**
- Operation + "Action" suffix: `CreateUserAction`, `SendEmailAction`
- Verb-noun format: Clear what it does
- Namespace by domain: `Users/`, `Orders/`, `Payments/`

**Method names:**
- `handle()` - Primary method for the action
- Specific methods for complex operations: `validateUser()`, `persistToDatabase()`

**File structure:**
- One action per file
- Directory per domain/entity type
- `app/Actions/` root directory

### Advanced Patterns

**Action with Transaction:**
```php
final readonly class CreateOrderAction {
    public function __construct(private OrderRepository $orders) {}

    public function handle(array $data): Order {
        return DB::transaction(function () use ($data) {
            $order = $this->orders->create($data);
            $this->orders->attachItems($order->id, $data['items']);
            return $order;
        });
    }
}
```

**Action with Events:**
```php
final readonly class ProcessPaymentAction {
    public function __construct(private PaymentGateway $gateway) {}

    public function handle(Order $order): Payment {
        $payment = $this->gateway->process($order->total);

        // Dispatch event instead of tightly coupling logic
        event(new PaymentProcessed($order, $payment));

        return $payment;
    }
}
```

**Action Composition:**
```php
final readonly class CompleteOrderAction {
    public function __construct(
        private ProcessPaymentAction $processPayment,
        private SendConfirmationAction $sendConfirmation,
    ) {}

    public function handle(Order $order): Order {
        $payment = $this->processPayment->handle($order);
        $this->sendConfirmation->handle($order);

        return $order->markComplete();
    }
}
```

## React Component & Hook Pattern

### Component Structure

```
src/
├── components/          # Reusable UI components
│   ├── button.tsx
│   ├── card.tsx
│   └── form-field.tsx
├── sections/            # Composite sections (headers, forms, features)
│   ├── user-profile-form.tsx
│   └── order-summary.tsx
├── layouts/             # Page layouts
│   ├── dashboard-layout.tsx
│   └── auth-layout.tsx
└── pages/               # Route pages
    ├── users/
    │   ├── index.tsx
    │   └── show.tsx
    └── orders/
        ├── index.tsx
        └── create.tsx
```

### Component Convention

**Small, focused components (<100 lines):**
```tsx
interface ButtonProps {
    label: string;
    onClick: () => void;
    variant?: 'primary' | 'secondary';
}

export function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
    return (
        <button
            className={`btn btn-${variant}`}
            onClick={onClick}
        >
            {label}
        </button>
    );
}
```

**Composite sections (reusable blocks):**
```tsx
interface UserFormProps {
    user?: User;
    onSubmit: (data: UserData) => Promise<void>;
}

export function UserProfileForm({ user, onSubmit }: UserFormProps) {
    const form = useUserForm(user);

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        await onSubmit(form.data);
    };

    return (
        <form onSubmit={handleSubmit}>
            <FormField label="Name" value={form.data.name} />
            <FormField label="Email" value={form.data.email} />
            <button type="submit">Save</button>
        </form>
    );
}
```

### Custom Hook Convention

**Naming:**
- `use` + PascalCase: `useUserForm`, `useFetchUsers`, `useAuthContext`
- Describe what it does: `useFormValidation`, `useLocalStorage`, `usePaginatedData`

**Pattern:**
```tsx
function useUserForm(initialUser?: User) {
    const [data, setData] = useState(initialUser || {});
    const [errors, setErrors] = useState({});

    const validate = () => {
        // Validation logic
    };

    const submit = async () => {
        // Submit logic
    };

    return { data, setData, errors, validate, submit };
}

// Usage
function MyComponent() {
    const form = useUserForm(user);
    return <form onSubmit={form.submit}>...</form>;
}
```

### Composition Pattern

**Extract child components:**
```tsx
function UserDashboard({ userId }) {
    const user = useUserData(userId);

    return (
        <div className="dashboard">
            <UserHeader user={user} />
            <UserStats user={user} />
            <UserActivity user={user} />
        </div>
    );
}

// Separate components
function UserHeader({ user }) {
    return <header>{user.name}</header>;
}

function UserStats({ user }) {
    return <div>Stats content</div>;
}

function UserActivity({ user }) {
    return <div>Activity content</div>;
}
```

## Vue Composition & Pattern

### Component Structure

```
src/
├── components/          # Reusable UI components
├── views/              # Route views/pages
├── composables/        # Reusable composition functions
│   ├── useUserForm.ts
│   └── useFetchData.ts
└── services/           # API client services
    └── userService.ts
```

### Composable Convention

```typescript
// composables/useUserForm.ts
import { ref, computed } from 'vue';

export function useUserForm(initialUser = null) {
    const data = ref(initialUser || {});
    const errors = ref({});

    const validate = () => {
        // Validation logi

Related in Design