Proč je vzor služby v projektech Laravel nepořádek
Na stránkách Vzor služby začalo jako ušlechtilá myšlenka: oddělit obchodní logiku od kontrolérů, aby bylo vše čisté. V reálných projektech Laravel se však stala jednou z nejvíce zneužívaných a nepochopených architektonických možností. To, co mělo přinést přehlednost, se často mění ve skládku špatně umístěné logiky.
Probereme si, proč k tomu dochází, jak to vede k chaosu v kódu a jaké lepší vzory můžete použít místo toho.
Hlavní problém: ztratil svůj smysl
V projektech Laravel je termín Služba se stala univerzální nálepkou. Když si vývojáři nejsou jisti, kam umístit logiku, často vytvoří třídu "služba" jako rychlé řešení. Časem se tyto třídy rozrostou bez struktury a účelu.
V praxi se děje toto:
1. Služby podobné úložišti
Vývojáři používají "službu" pro zpracování databázových dotazů, která duplikuje to, k čemu je určen správný vzor úložiště (tj. Eloquent).
// app/Services/UserService.phpclass UserService { public function all() { return User::all(); } public function active() { return User::where('active', true)->get(); }}
To nepřináší žádnou přidanou hodnotu, je to jen zbytečný obal kolem modelu. V systému Laravel, vzor úložiště vůbec nepotřebujete.. Eloquent ORM již is vrstvy úložiště. Poskytuje abstrakci dotazů, mapování dat a expresivní syntaxi.
Logika by měla být přímo v modelu pomocí obory dotazů or vlastní tvůrci dotazů.
// app/Models/User.phpclass User extends Model { public function scopeActive($query) { return $query->where('active', true); }}
$users = User::active()->get();
Je čistší, čitelnější a dokonale odpovídá konvencím Laravelu. Není potřeba žádné další úložiště nebo vrstva služeb, která by obalovala Eloquent.
2. Služby subkontrolérů
Někteří vývojáři přenášejí veškerou práci s řadiči do třídy služeb. Kontrolér se tak stává prázdnou skořápkou, zatímco služba se stává přebujelým pseudokontrolérem.
// app/Services/UserService.phpclass UserService { public function store(array $data) { $user = User::create($data); UserRegistered::dispatch($user); // more user creation logic ... return $user; }} // app/Http/Controllers/UserController.phpclass UserController { public function store(Request $request, UserService $service) { return $service->store($request->validated()); }}
Místo toho udělejte toto:
// app/Actions/CreateUser.phpclass CreateUser { public function execute(array $data): User { return User::create($data); }} // app/Http/Controllers/UserController.phpclass UserController { public function store(StoreUserRequest $request, CreateUser $action) { $user = $action->execute($request->validated()); UserRegistered::dispatch($user); return response()->json($user); }}
3. Modelové akční služby
Častým zneužitím vzoru služby je. "Modelová akční služba" přístup, kdy vývojáři seskupují všechny operace související s jedním modelem do jedné obrovské třídy.
// app/Services/UserService.phpclass UserService { public function create(array $data) { ... } public function deactivate(User $user) { ... } public function update(User $user) { ... }}
Na první pohled vypadá organizovaně, protože vše, co se týká User model je na jednom místě. Ale jak projekt roste, stává se z toho problém. přebujelá, neudržovatelná třída. Každá metoda se zabývá různými problémy, jako je obchodní logika, oznámení, integrace a změny stavu, a to vše je spojeno dohromady.
Místo toho použijte Akční vzor oddělit každé chování do vlastní zaměřené třídy.
app/├── Actions/│ ├── ActivateUser.php│ ├── DeactivateUser.php│ ├── PromoteUser.php│ └── SendWelcomeEmail.php
Každá akce zpracovává pouze jednu úlohu, což z vašeho kódu činí:
- Snadnější testování
- Jednodušší uvažování
- Konzistentní struktura
Příklad:
// app/Actions/ActivateUser.phpclass ActivateUser { public function execute(User $user): User { $user->update(['active' => true]); return $user; }}
Pak ve vrstvě řadiče nebo služby:
public function activate(User $user, ActivateUser $action){ return response()->json($action->execute($user));}
Toto oddělení udržuje logiku modulární, předvídatelnou a dokonale sladěnou s čistým stylem architektury Laravel.
4. Komunální služby nebo služby
Dalším častým zneužitím je přeměna servisních tříd na užitkové kontejnery, generické pomocníky, které vykonávají nesouvisející, průřezové úlohy.
// app/Services/HelperService.phpclass HelperService { public function formatDate($date) { ... } public function slugify($string) { ... } public function toMoneyFormat($value) { ... }}
Na první pohled se zdá, že je vhodné centralizovat "užitečné" funkce. Ale v okamžiku, kdy sem začnete přidávat náhodné pomocníky, se tato třída stane různé skládky, neurčitá "služba", která nikam nepatří.
Lepší přístup:
app/├── Utilities/│ ├── CurrencyConverter.php│ └── HtmlToMarkdownConverter.php
Každý pomocník má jasná, jednotná odpovědnost a popisný název.
Hlavní myšlenkou je: Komunální služby nejsou služby. Jsou to pomocníci, kteří jsou lehké, kontextově agnostické nástroje a zaslouží si svůj vlastní domov.
5. Integrační služby třetích stran
Integrační logika (například Stripe nebo AWS) často končí v obecných "službách", což opět zvyšuje zmatek.
// app/Services/StripeService.phpclass StripeService { public function charge(User $user, $amount) { ... }}
Ty patří do vyhrazeného Integrace oboru názvů nebo adresáře.
app/├── Integrations/│ └── Stripe/│ ├── StripeClient.php│ ├── StripeCharge.php│ └── StripeWebhookHandler.php
Každá integrace má správnou strukturu a účel.
Proč je to nebezpečné
Když se vzor služby stane univerzálním:
- Pojmenování ztrácí smysl - "Co tato služba vlastně dělá?" je častá otázka.
- Údržba se stává bolestivou - Změna logiky znamená hledání v libovolných metodách služby.
- Testování je těžší - Příliš obecné třídy závisí na příliš mnoha částech aplikace.
- Přestávky v týmové komunikaci - Každý vývojář si pojem "služba" vykládá jinak.
Lepší přístup: Použijte správný vzor pro správnou práci
Laravel vám poskytuje flexibilitu, díky níž můžete svou logiku uspořádat přehledně. Místo jedné neurčité vrstvy "služby" používejte konkrétní vzory a pojmenování, které vyjadřují záměr.
| Zneužití | Problém | Správný vzor | Příklad adresáře |
|---|---|---|---|
| Služby podobné úložištím | Logika duplicitních dat | Použití vestavěného úložiště Eloquent ORM (built-in Repository) | app/Models/User.php |
| Služby subkontrolérů | Přemrštěná, chybně umístěná logika HTTP | Akční vzor | app/Actions/CreateUser.php |
| Model Action Services | Smíšené chování v jedné třídě | Akční vzor | app/Actions/ActivateUser.php |
| Komunální/podpůrné služby | Odkladiště pro pomocníky | Podpůrné třídy / pomocníci | app/Support/DateHelper.php |
| Integrační služby třetích stran | Smíšená integrace a doménová logika | Integrační vrstva | app/Integrations/Stripe/StripeCharge.php |
Příklad: Čistá struktura bez servisního nepořádku
app/├── Actions/│ └── CreateUser.php├── Http/│ └── Controllers/│ └── UserController.php├── Integrations/│ └── Stripe/│ └── StripeCharge.php├── Models/│ └── User.php└── Utilities/ └── CurrencyConverter.php
Díky této struktuře vždy víte, kam logiku umístit a kde ji později najít. Obecný adresář "Service" není vůbec potřeba.
Závěrečné myšlenky
Vzor Service se v Laravelu stal nepořádkem, protože přestal mít smysl. Stal se z něj odpadkový koš pro kód, o kterém vývojáři nevěděli, kam ho zařadit. Řešením není neustále vylepšovat koncept "služby", ale je třeba přestat ji používat jako výchozí.
Použijte Eloquent ORM jako úložiště, Akce pro akce prováděné na modelech, Integrace pro externí rozhraní API a Podpora pro sdílené pomocníky nebo nástroje. Nechť struktura a pojmenování sdělují záměr. Takto vytvoříte projekty Laravel, které zůstanou čisté, udržovatelné a škálovatelné.