Service Layer در مقابل Action Class — کدام را در Laravel انتخاب کنیم؟
در Laravel دو الگوی محبوب برای خروج logic از Controller داریم: Service Layer (یک کلاس با چند متد مرتبط مثل PostService) و Action Class (یک کلاس یک use case مثل CreatePostAction). بحث forum اغلب «کدام بهتر» است — جواب: بستگی به اندازه تیم و پیچیدگی domain دارد.
Service Layer چیست؟
class PostService
{
public function __construct(private PostRepositoryInterface $posts) {}
public function create(CreatePostData $data, User $author): Post { /* ... */ }
public function update(Post $post, UpdatePostData $data): Post { /* ... */ }
public function publish(Post $post): Post { /* ... */ }
}
گروه عملیات روی یک aggregate — معماری لایهای.
Action Class چیست؟
class CreatePostAction
{
public function handle(CreatePostData $data, User $author): Post { /* ... */ }
}
Action classes — یک فایل یک مسئولیت use case.
مقایسه سریع
| معیار | Service | Action |
|---|---|---|
| تعداد فایل | کمتر | بیشتر |
| یافتن use case | scroll متدها | نام فایل = use case |
| پروژه کوچک | راحتتر | شاید overkill |
| پروژه بزرگ | risk god service | scale بهتر |
| تست | mock یک service | تست یک action ایزوله |
| Route invokable | معمولاً نه | بله با __invoke |
کی Service؟
- CRUD ساده یک domain کوچک
- تیم ۱–۲ نفره — سرعت مهمتر از ساختار
- متدها واقعاً cohesive (همه درباره Post)
کی Action؟
- use case با مراحل زیاد (transaction، event، queue)
- نیاز به traceability (لاگ کدام عملیات)
- تیم بزرگ — conflict کمتر روی یک فایل بزرگ
ترکیب هر دو — رایج و سالم
class PostService
{
public function __construct(
private CreatePostAction $create,
private PublishPostAction $publish,
) {}
public function createAndPublish(CreatePostData $data, User $user): Post
{
$post = $this->create->handle($data, $user);
return $this->publish->handle($post);
}
}
Service نازک orchestrator؛ Actionها executable. یا Service per subdomain و Action برای complex flows.
Anti-patterns
- God Service — ۲۰۰۰ خط همه چیز
- Anemic Action — فقط
Post::create($data)wrap - Service در Service در Service — بدون مرز واضح
- static Service — تست و DI میشکند
ساختار پوشه پیشنهادی
app/Services/PostService.php // اختیاری facade نازک
app/Actions/Posts/CreatePostAction.php
app/Actions/Posts/PublishPostAction.php
app/DTO/Posts/CreatePostData.php
Repository کجا صدا زده شود؟
داخل Action یا Service — نه Controller. Repository.
Filament و Service/Action
Resource method میتواند Action را inject کند — logic یکسان web و admin.
جمعبندی
شروع با Service ساده OK؛ وقتی فایل شلوغ شد split به Action. DTO: DTO. VO: Value Object.