tezu memo blog

日々行った作業をメモしていきます

37signals Dev — Vanilla Rails is plenty を読んだ ServiceじゃなくてMix-inで良いのかも知れない

dev.37signals.com

自身のフレームワーク経験

感想

色々な問題が解決するので、Mix-inを試してみようと思いました

命名問題

recording.incinerate
Recording::Incineration.new(recording).run
Recording::IncinerationService.execute(recording)

it feels more natural, like plain English. It feels more Ruby.

{DomainName}Service.execute だと、使う側は仕様が分かり辛い、作る側はServiceの単位や名称を考えるのが辛い問題があるので、Mix-inの圧勝

正直、Service層を追加してから毎回悩んでます

Anemic Domain Model問題

Don’t lean too heavily toward modeling a domain concept as a Service. Do so only if the circumstances fit. If we aren’t careful, we might start to treat Services as our modeling “silver bullet.” Using Services overzealously will usually result in the negative consequences of creating an Anemic Domain Model, where all the domain logic resides in Services rather than mostly spread across Entities and Value Objects

Modelの属性しか操作していないServiceのfunctionがあったら、適宜Modelに移動している。Serviceが無ければModelに実装するので、この作業は不要になる

移譲問題

Tons of boilerplate code because many of these application-level elements simply delegate the operation to some domain entity. Remember that the application layer should not contain business rules. It just coordinates and delegates work to domain objects in the layer below.

マスタメンテ系の単純な画面だと、ServiceがModelに移譲するだけになるので、Service層を使わない判断をしている。これも、Serviceが無ければModelに実装するので、この作業は不要になる

LaravelのRoute Model Bindingを使用すると、LaravelがControllerとModelを紐付けするのでServiceを強制すると冗長的なコードになってしまう

laravel.com

  • Service無し
<?php
use App\Models\User;
 
// Route definition...
Route::get('/users/{user}', [UserController::class, 'show']);
 
// Controller method definition...
public function show(User $user)
{
    return view('user.profile', ['user' => $user]);
}
  • Service有り
<?php
public function show(int $userId)
{
    $service = app(UserService::class);
    $user = $service->findBy($userId);
    return view('user.profile', ['user' => $user]);
}