$30 off During Our Annual Pro Sale. View Details »

Unit of Workパターンで永続化とトランザクションを制御する

shimabox
September 16, 2023

Unit of Workパターンで永続化とトランザクションを制御する

PHPカンファレンス沖縄2023 Track B での発表資料です。

shimabox

September 16, 2023
Tweet

More Decks by shimabox

Other Decks in Programming

Transcript

  1. Unit of Workパターンで

    永続化とトランザクションを制
    御する

    2023/09/16

    by しまぶ@shimanox 


    View Slide

  2. 自己紹介

    View Slide

  3. コンフリクトを恐れない

    アジェンダ

    自己紹介

    View Slide

  4. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Workパターン サンプル

    4. 所感

    5. まとめ

    アジェンダ


    View Slide

  5. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Workパターン サンプル

    4. 所感

    5. まとめ

    1. Unit of Workパターンとは


    View Slide

  6. 1. Unit of Workパターンとは

    Martin Fowler氏によると、


    “ビジネストランザクションによって影響を受ける
    オブジェクトのリストを保持し、変更の書き込みと
    同時実行による問題の解決を調整する。”



    Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

    P184(※機械翻訳後に意訳)


    View Slide

  7. 1. Unit of Workパターンとは

    なるほど、ちょっとわからん

    😇


    View Slide

  8. 1. Unit of Workパターンとは

    ちょうぜつ

    Advent Calendar 2022 6日目


    UnitOfWork


    https://qiita.com/tanakahisateru/items/b0c441c
    4540e84fe6dea


    View Slide

  9. 1. Unit of Workパターンとは

    ● DomainModelの変更を都度DBに反映せず、

    ビジネスロジック完了後にまとめて反映

    ● 変更は監視され、最小のSQLで同期される

    ● ActiveRecordとUnit of Workは排他的

    ● DataMapperはUnit of Workと自然に共存する


    ※ 自分なりに要約したもの


    View Slide

  10. 1. Unit of Workパターンとは

    なんとなく、わかるぞ

    😀


    View Slide

  11. 1. Unit of Workパターンとは

    たぶん、ActiveRecord ディスられている

    (偏見)

    😀


    View Slide

  12. 1. Unit of Workパターンとは

    ここまでで分かったこと


    ● ビジネスロジックでの変更内容を覚えておいて(ここではDBに関して無関
    心)、最後にDBへ反映させるもの

    ● つまり、単純にトランザクションが短くなる

    ○ DBへ反映させるときだけだから

    ○ パフォーマンスが良くなる

    ● DataMapper が肝っぽい

    ● ActiveRecord と Unit of Workは排他的


    View Slide

  13. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Workパターン サンプル

    4. 所感

    5. まとめ

    2. ActiveRecord vs DataMapper


    View Slide

  14. 2. ActiveRecord vs DataMapper

    ActiveRecord

    ● みんな大好き(?)

    ● 好きなときにDB触れる

    ○ 行をオブジェクトとして表現し、そのオブジェクトにDB操作のロジックを持つ

    ○ CRUD操作するためのメソッドや機能がついている

    ● ドメインのビジネスロジックとデータベース操作のロジックが混在する

    ● DBのことを知っている


    View Slide

  15. 2. ActiveRecord vs DataMapper

    DataMapper

    ● DBのレコードを表すもの(Entity)と、それらのライフサイクルを管理するもの(エンティティマ
    ネージャ)に分かれている

    ● Entityはビジネスロジックやアプリケーションのドメインモデルを表現する 

    ● DataMapperは、EntityとDBとの間のマッピングや操作を担当する 

    ○ デザインパターンのひとつ

    ○ インピーダンスミスマッチ とかいうやつ

    ■ 現実世界とアプリケーションとのうんたらかんたら

    ○ エンティティマネージャは裏方、仲介役

    ● Entity自体はDBのことを知らない/依存していない (諸説あり)

    ○ アノテーションは書かれているが


    View Slide

  16. 2. ActiveRecord vs DataMapper

    ActiveRecord

    ● 「何をすべきか」「どのように」を一つのオブジェクト(ActiveRecord)が担当

    ● 自分が主役


    DataMapper

    ● 「オブジェクト(Entity)が何をすべきか」に集中でき、「どのように」の部分は

    エンティティマネージャ(リポジトリ)が担当

    ● Entityはエンティティマネージャ(リポジトリ)がいないと
    DBに対して何もできない


    View Slide

  17. 2. ActiveRecord vs DataMapper

    サンプルを書いてみよう

    (PHP 8.2.10, Laravel 10.21.0 使う)


    View Slide

  18. 2. ActiveRecord vs DataMapper

    ActiveRecord

    ● Eloquent


    DataMapper

    ● Doctrine

    ○ laravel-doctrine


    View Slide

  19. 2. ActiveRecord vs DataMapper

    DataMapper

    ● Doctrine

    ○ laravel-doctrine/orm

    ■ 

    ■ 自分が試したときは、-W をつけないと入らなかった

    ■ `Downgrading doctrine/lexer (3.0.0 => 2.1.0)` された

    ○ laravel-doctrine/migrations

    ■ 


    $ composer require laravel-doctrine/orm:^2.0@dev -W
    $ composer require laravel-doctrine/migrations:^3.0@dev

    View Slide

  20. 2. ActiveRecord vs DataMapper

    DataMapper

    ● Doctrine

    ○ laravel-doctrine/orm

    ■ 

    ■ 自分が試したときは、-W をつけないと入らなかった

    ■ `Downgrading doctrine/lexer (3.0.0 => 2.1.0)` された

    ○ laravel-doctrine/migrations

    ■ 


    $ composer require laravel-doctrine/orm:^2.0@dev -W
    $ composer require laravel-doctrine/migrations:^3.0@dev

    View Slide

  21. 2. ActiveRecord vs DataMapper

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  22. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  23. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord) Doctrine (DataMapper)
    楽すぎて草

    (無邪気)

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  24. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  25. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    EntityManagerを呼ぶ

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  26. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    関連付けはEntityに対してア
    ノテーションを定義

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  27. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    こういうやつ

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    namespace App\Entities;
    /**
    * @ORM\Entity
    * @ORM\Table(name="members")
    * @ORM\Entity(repositoryClass=MemberRepository::class)
    */
    class Member
    {
    /**
    * @ORM\Id
    * @ORM\GeneratedValue
    * @ORM\Column(type="integer")
    */
    private $id;
    // 〜 略
    /**
    * @ORM\OneToMany(targetEntity="Post",
    mappedBy="member")
    */
    private $posts;

    View Slide

  28. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    // この時点では、まだデータベースには何も保存されていない
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    EntityManagerが変更をマー
    キング(監視)

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  29. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    // この時点では、まだデータベースには何も保存されていない
    $em->persist($member);
    $em->persist($post);
    // flush()を呼び出すと、監視されている全てのエンティティの変更が一
    度にデータベースに適用される
    $em->flush();
    Doctrine (DataMapper)
    ここでしかデータベースへの
    保存(トランザクション)は発生
    しない

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  30. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    $member = new Member();
    $member->name = "Alice";
    $member->save(); // この時点でデータベースに保存
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post); // この時点でデータベースに保存
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    // この時点では、まだデータベースには何も保存されていない
    $em->persist($member);
    $em->persist($post);
    // flush()を呼び出すと、監視されている全てのエンティティの変更が一
    度にデータベースに適用される
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    ふむふむ、DBへの書き込
    み、トランザクションと...


    View Slide

  31. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    DB::transaction(function () {
    $member = new Member();
    $member->name = "Alice";
    $member->save();
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em"); // EntityManager
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post); // 関連付け
    // この時点では、まだデータベースには何も保存されていない
    $em->persist($member);
    $em->persist($post);
    // flush()を呼び出すと、監視されている全てのエンティティの変更が一
    度にデータベースに適用される
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    トランザクションで囲ってみま
    した


    View Slide

  32. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    DB::transaction(function () {
    $member = new Member();
    $member->name = "Alice";
    $member->save();
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    少し想像してみよう...


    View Slide

  33. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    DB::transaction(function () {
    $member = new Member();
    $member->name = "Alice";
    $member->save();
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  34. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    DB::transaction(function () {
    $member = new Member();
    $member->name = "Alice";
    $member->save();
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    ここでしかトランザクションが
    発生しない

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  35. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    DB::transaction(function () {
    $member = new Member();
    $member->name = "Alice";
    $member->save();
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->title = "A post by Alice";
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん
    え、3秒はトランザクションが
    はられてしまう...?

    Doctrine (DataMapper) ドクくん

    View Slide

  36. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    $member = new Member();
    $member->name = "Alice";
    // 長い処理
    $post = new Post();
    $post->title = "A post by Alice";
    DB::transaction(function () use ($member, $post) {
    $member->save();
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    トランザクションから脱出しま
    す


    View Slide

  37. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use App\Models\Post;
    use Illuminate\Support\Facades\DB;
    $member = new Member();
    $member->name = "Alice";
    // 長い処理
    $post = new Post();
    $post->title = "A post by Alice";
    // この間で、$member, $postはDBに気軽に触れる
    DB::transaction(function () use ($member, $post) {
    $member->save();
    $member->posts()->save($post);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use App\Entities\Post;
    $em = app("em");
    $member = new Member();
    $member->setName("Alice");
    // ここの間に、なにか長い処理があった場合
    // Postの内容をどこか別の場所から取るとか
    // sleep(3);
    $post = new Post();
    $post->setTitle("A post by Alice");
    $member->addPost($post);
    $em->persist($member);
    $em->persist($post);
    $em->flush();
    Doctrine (DataMapper)
    無邪気なActiveRecordがうろ
    つきだしたわね...

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  38. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  39. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    配列のメモリが心配


    View Slide

  40. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    そもそも、
    ["name" => $xxx,,,]
    って何?


    View Slide

  41. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    これが、みんな大好き連想配
    列というやつ!?


    View Slide

  42. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    データを作るための、やん
    ちゃな連想配列, stdClassが
    うまれやすい...!?


    View Slide

  43. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    ここでEntityに対する処理を
    して、persistしているだけ。

    ちゃんと会話していそう👀

    Doctrine (DataMapper) ドクくん
    Eloquent (ActiveRecord) アッコちゃん

    View Slide

  44. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    この中がもっと複雑であって
    も、ビジネスロジックだけに集
    中してモデルを変更 →
    persistするだけ

    Doctrine (DataMapper) ドクくん
    Eloquent (ActiveRecord) アッコちゃん

    View Slide

  45. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    何をしようとしているのかわ
    かりやすい

    Doctrine (DataMapper) ドクくん
    Eloquent (ActiveRecord) アッコちゃん

    View Slide

  46. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん
    永続化関連のクラスは特定
    の層でしか使えないような
    ルールもかけやすいかも。

    deptracとか


    View Slide

  47. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    use Illuminate\Support\Facades\DB;
    $members = [];
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $members[] = [
    "name" => $prop->name,
    "email" => $prop->mail_address,
    // ...
    ];
    }
    DB::transaction(function () use ($members) {
    (new Member())->insert($members);
    });
    Eloquent (ActiveRecord)
    use App\Entities\Member;
    use Doctrine\ORM\EntityManagerInterface;
    $em = app()->make(EntityManagerInterface::class);
    $memberProps = /** どこかからか取ってきた */;
    foreach ($memberProps as $prop) {
    $member = new Member();
    $member->setName($prop->name);
    $member->setEmail($prop->mail_address);
    // ...
    $em->persist($member);
    }
    $em->flush();
    Doctrine (DataMapper)
    最後にflushして確定してあげ
    ると、いい具合にしてくれる

    (もちろんメモリ量の心配はあ
    る)

    Eloquent (ActiveRecord) アッコちゃん Doctrine (DataMapper) ドクくん

    View Slide

  48. 2. ActiveRecord vs DataMapper

    いい具合とは?


    View Slide

  49. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    $em = app("em");
    // 登録
    $member = new Member();
    $member->setName("taro");
    $em->persist($member);
    // なんやかんや
    // 更新
    $member->setProfile("XXXXX");
    $em->persist($member);
    // なんやかんや
    // 不適切!削除
    $em->remove($member);
    // 確定
    $em->flush();
    Doctrine (DataMapper)
    ● メンバーを登録して
    ● なんやかんやして
    ● メンバーの情報を更新する
    ● なんやかんやして
    ● 情報に不適切なものがあればメンバーを削
    除する
    ユースケース

    View Slide

  50. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    $em = app("em");
    // 登録
    $member = new Member();
    $member->setName("taro");
    $em->persist($member);
    // なんやかんや
    // 更新
    $member->setProfile("XXXXX");
    $em->persist($member);
    // なんやかんや
    // 不適切!削除
    $em->remove($member);
    // 確定
    $em->flush();
    Doctrine (DataMapper) 実行されるクエリ
    何も実行されない


    View Slide

  51. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    $em = app("em");
    // 登録
    $member = new Member();
    $member->setName("taro");
    $em->persist($member);
    // なんやかんや
    // 更新
    $member->setProfile("XXXXX");
    $em->persist($member); // $memberはマーク済みなのでしなくてもいい
    // なんやかんやあって、削除しない
    // 確定
    $em->flush();
    Doctrine (DataMapper)
    {
    "sql": "INSERT INTO members (name,
    profile) VALUES (?, ?)",
    "params": {
    "1": "taro",
    "2": "XXXXX"
    },
    "types": {
    "1": 2,
    "2": 2
    }
    }
    実行されるクエリ
    1回


    View Slide

  52. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    $em = app("em");
    // 登録
    $member = new Member();
    $member->setName("taro");
    $em->persist($member);
    // なんやかんや
    // 更新
    $member->setProfile("XXXXX");
    $em->persist($member); // $memberはマーク済みなのでしなくてもいい
    // なんやかんやあって、削除しない
    // 確定
    $em->flush();
    Doctrine (DataMapper)
    {
    "sql": "INSERT INTO members (name,
    profile) VALUES (?, ?)",
    "params": {
    "1": "taro",
    "2": "XXXXX"
    },
    "types": {
    "1": 2,
    "2": 2
    }
    }
    実行されるクエリ
    自然に書けそう


    View Slide

  53. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    // 登録
    $member = new Member();
    $member->name = "taro";
    $member->save();
    // なんやかんや
    // 更新
    $member->profile = "XXXXX";
    $member->save();
    // なんやかんや
    // 不適切!削除
    $member->delete();
    Eloquent (ActiveRecord)
    insert into `members` (`name`) values (taro)
    update `members` set `profile` = XXXXX where `id` = 1
    delete from `members` where `id` = 1
    実行されるクエリ
    3回


    View Slide

  54. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    // 登録
    $member = new Member();
    $member->name = "taro";
    $member->save();
    // なんやかんや
    // 更新
    $member->profile = "XXXXX";
    $member->save();
    // なんやかんやあって、削除しない
    Eloquent (ActiveRecord)
    insert into `members` (`name`) values (taro)
    update `members` set `profile` = XXXXX where `id` = 1
    実行されるクエリ
    2回


    View Slide

  55. 2. ActiveRecord vs DataMapper

    use App\Models\Member;
    // 登録
    $member = new Member();
    $member->name = "taro";
    $member->save();
    // なんやかんや
    // 更新
    $member->profile = "XXXXX";
    $member->save();
    // なんやかんやあって、削除しない
    Eloquent (ActiveRecord)
    insert into `members` (`name`) values (taro)
    update `members` set `profile` = XXXXX where `id` = 1
    実行されるクエリ
    無駄なクエリを流さないため
    に、工夫が必要。

    その工夫が煩雑さをうむ?


    View Slide

  56. 2. ActiveRecord vs DataMapper

    Doctrine (DataMapper)


    一連のビジネスロジックが完了した後に、

    最終的な結果をまとめて

    いい具合に反映させる


    View Slide

  57. 1. (再掲) Unit of Workパターンとは

    ● DomainModelの変更を都度DBに反映せず、

    ビジネスロジック完了後にまとめて反映

    ● 変更は監視され、最小のSQLで同期される

    ● ActiveRecordとUnit of Workは排他的

    ● DataMapperはUnit of Workと自然に共存する


    View Slide

  58. 2. ActiveRecord vs DataMapper

    お気づきになったであろうか...


    View Slide

  59. 2. ActiveRecord vs DataMapper

    DoctrineがUnit of Workの

    パターンを組み合わせて

    実装されているのである


    View Slide

  60. 2. ActiveRecord vs DataMapper


    こんなインターフェイスもあるし😎

    namespace Doctrine\ORM;
    interface EntityManagerInterface extends ObjectManager
    {
    /**
    * Gets the UnitOfWork used by the EntityManager to coordinate operations.
    *
    * @return UnitOfWork
    */
    public function getUnitOfWork();
    }

    View Slide

  61. 2. ActiveRecord vs DataMapper


    UnitOfWorkも持っている😎

    namespace Doctrine\ORM;
    class UnitOfWork implements PropertyChangedListener
    {
    }

    View Slide

  62. 2. ActiveRecord vs DataMapper

    Doctrine is Unit of Work.


    View Slide

  63. 2. ActiveRecord vs DataMapper

    みんなDoctrineを使おっ!?

    そしたらUnit of Workだぞっ💛

    😍


    View Slide

  64. 2. ActiveRecord vs DataMapper

    完


    View Slide

  65. 2. ActiveRecord vs DataMapper

    完

    もうちょいやります

    一

    一


    View Slide

  66. 2. ActiveRecord vs DataMapper

    監視しておいて最後に保存


    View Slide

  67. 2. ActiveRecord vs DataMapper

    並行処理は?

    最後にあぼーんしない?


    View Slide

  68. 1. Unit of Workパターンとは

    Martin Fowler氏によると、


    “Unit of Work の重要な点は、コミットの時に、Unit of
    Work が何をすべきかを決定することである。トランザク
    ションを開き、同時実行チェックを実行し(悲観的ロック、
    または楽観的ロックを使用)、変更をデータベースに書き
    出す。”




    Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

    P184(※機械翻訳後に意訳) 

    2. ActiveRecord vs DataMapper


    View Slide

  69. 1. Unit of Workパターンとは

    ちょうぜつUnitOfWork によると、


    “トランザクションの ACID 性と、複数の並行処理がデー
    タの矛盾を発生させないようにするロックとは、別の問題
    です。並行処理のロックについては、別のパターンで述
    べられています。”




    ちょうぜつ Advent Calendar 2022 6日目

    2. ActiveRecord vs DataMapper


    View Slide

  70. 2. ActiveRecord vs DataMapper

    Unit of Workが解決することではない

    別のパターンで解決


    View Slide

  71. 1. Unit of Workパターンとは

    楽観的ロックと悲観的ロック(ざっくりと)


    ● 楽観的ロック(早いもの勝ち)

    ○ Doctrineだと、@Versionアノテーションをエンティティのフィールドに付ける

    ○ Eloquentだと、ひと工夫が必要そう

    ● 悲観的ロック(俺のもの)

    ○ Doctrineだと、EntityManagerのlockメソッドを使用

    ■ LockMode::PESSIMISTIC_READ(読み取り), LockMode::PESSIMISTIC_WRITE(書き込み)

    ○ Eloquentだと、sharedLock()(共有)、lockForUpdate()(専有)

    ● うっ...頭痛が...

    2. ActiveRecord vs DataMapper


    View Slide

  72. 1. Unit of Workパターンとは

    ここまでで分かったこと


    ● Doctrine(DataMapper)がUnit of Workだった

    ● ActiveRecordで同じことをやろうとすると、意識することが多い

    ○ ActiveRecordのよさが消えそう

    ● Unit of Workは管理者がいて、そいつが管理する

    ○ ActiveRecordは自由

    ○ 排他的なの分かる

    ● 並行処理のロックについては、別のパターンで解決

    2. ActiveRecord vs DataMapper


    View Slide

  73. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Workパターン サンプル

    4. 所感

    5. まとめ

    3. Unit of Workパターン サンプル


    View Slide

  74. 3. Unit of Workパターン サンプル

    永続化処理をユースケース層から分離

    ● 永続化、トランザクションを効率的に行えるのは分かった

    ● が、今のままだとユースケース層に永続化の知識が漏れている

    ○ persist(); , flush(); とか

    ○ Laravelだと、 DB::transaction(function(){}); しているところ

    ● ユースケース(アプリケーションサービス)層では、永続化の知識に依存したくないはず

    ○ 原理主義者

    ● Laravel

    ○ Eloquent → Eloquent/UoW風 → Doctrine/UoW

    View Slide

  75. 2. ActiveRecord vs DataMapper

    Doctrine (DataMapper) ユースケース

    ● CSVから行を読み込んで、メンバー情報を更
    新する

    ● 登録、削除、更新のケースが考えられる

    ● この例はリポジトリの中でEloquentを使って
    いる

    リファクタポイント

    ● 永続化の知識が見えているところ

    ○ 別に悪いことじゃないけど

    ○ 原理主義でいくのなら

    3. Unit of Workパターン サンプル

    class UseCase
    {
    public function __construct(
    private readonly FileInterface $file,
    private readonly MemberRepositoryInterface $memberRepository
    ) { }
    public function __invoke(FileParameterInterface $parameter)
    {
    /** @var Generator CSVを読み込んでオブジェクト化したやつ */
    $csv = $this->file->read($parameter->getPathName());
    foreach ($csv as $row) {
    if ($row->shouldDelete()) {
    $deleteMembers[] = $row->toArray();
    continue;
    }
    if ($shouldUpdate) {
    $updateMembers[] = $row->toArray();
    continue;
    }
    $insertMembers[] = $row->toArray();
    }
    // 永続化
    DB::transaction(function () use ($insertMembers, ...) {
    $this->memberRepository->insert($insertMembers);
    $this->memberRepository->delete($deleteMembers);
    $this->memberRepository->update($updateMembers);
    });
    }
    }

    View Slide

  76. 2. ActiveRecord vs DataMapper

    これを参考にやってみる

    https://martinfowler.com/eaaCatalog/Unit of Work.html

    3. Unit of Workパターン サンプル


    View Slide

  77. 2. ActiveRecord vs DataMapper

    interface EloquentUowServiceInterface
    {
    public function createMembers(array $member): void;
    public function updateMembers(array $member): void;
    public function deleteMembers(array $member): void;
    public function commit(): void;
    }
    // arrayですまんな...
    Interface
    class EloquentUowService implements EloquentUowServiceInterface
    {
    private array $createMembers = [];
    private array $updateMembers = [];
    private array $deleteMembers = [];
    public function __construct(
    private readonly MemberRepositoryInterface $memberRepository
    ) { }
    public function createMembers(array $member): void
    {
    $this->createMembers[] = $member;
    }
    public function updateMembers(array $member): void { /** 割愛 */ }
    public function deleteMembers(array $member): void { /** 割愛 */ }
    public function commit(): void
    {
    // 永続化
    DB::transaction(function () {
    $this->memberRepository->insert($this->createMembers);
    $this->memberRepository->update($this->updateMembers);
    $this->memberRepository->delete($this->deleteMembers);
    });
    }
    }
    具象
    3. Unit of Workパターン サンプル


    View Slide

  78. 2. ActiveRecord vs DataMapper

    リファクタ後

    ● 永続化の知識が見えなくなった

    ○ どこかにcommitしているんだろうなぁと
    いう抽象具合

    ● 更新するためのデータを溜め込んで渡すの
    は変わらない

    ○ 配列をどこかにひきづりまわしたくない

    ● ラッパーが増えただけ?

    3. Unit of Workパターン サンプル

    class UseCase
    {
    public function __construct(
    private readonly FileInterface $file,
    private readonly EloquentUowServiceInterface $eloquentUowService,
    ) { }
    public function __invoke(FileParameterInterface $parameter)
    {
    /** @var Generator CSVを読み込んでオブジェクト化したやつ */
    $csv = $this->file->read($parameter->getPathName());
    foreach ($csv as $row) {
    if ($row->shouldDelete()) {
    $this->eloquentUowService->deleteMembers($row->toArray());
    continue;
    }
    if ($shouldUpdate) {
    $this->eloquentUowService->updateMembers($row->toArray());
    continue;
    }
    $this->eloquentUowService->createMembers($row->toArray());
    }
    $this->eloquentUowService->commit();
    }
    }

    View Slide

  79. 2. ActiveRecord vs DataMapper


    Doctrineだとどうだろう


    3. Unit of Workパターン サンプル


    View Slide

  80. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    interface UowServiceInterface
    {
    public function createMembers(Member $entity): void;
    public function updateMembers(Member $entity): void;
    public function deleteMembers(Member $entity): void;
    public function commit(): void;
    }
    Interface
    class UowService implements UowServiceInterface
    {
    public function __construct(
    private readonly EntityManagerInterface $entityManager
    ) { }
    public function createMembers(Member $entity): void
    {
    $this->entityManager->persist($entity);
    }
    public function updateMembers(Member $entity): void
    {
    $this->entityManager->persist($entity);
    }
    public function deleteMembers(Member $entity): void
    {
    $this->entityManager->remove($entity);
    }
    public function commit(): void
    {
    // 永続化
    $this->entityManager->flush();
    }
    }
    具象
    3. Unit of Workパターン サンプル


    View Slide

  81. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    interface UowServiceInterface
    {
    public function createMembers(Member $entity): void;
    public function updateMembers(Member $entity): void;
    public function deleteMembers(Member $entity): void;
    public function commit(): void;
    }
    Interface
    class UowService implements UowServiceInterface
    {
    public function __construct(
    private readonly MemberRepositoryInterface $memberRepository
    ) { }
    public function createMembers(Member $entity): void
    {
    $this->memberRepository->add($entity);
    }
    public function updateMembers(Member $entity): void
    {
    $this->memberRepository->update($entity);
    }
    public function deleteMembers(Member $entity): void
    {
    $this->memberRepository->remove($entity);
    }
    public function commit(): void
    {
    // 永続化
    $this->memberRepository->save();
    }
    }
    具象
    3. Unit of Workパターン サンプル

    リポジトリ使ってもいい

    (こうすればInterfaceでも注入できるはず)

    // ServiceProvider...
    public function register(): void
    {
    $this->app->bind(
    MemberRepositoryInterface::class, fn (Application $app) =>
    $app["em"]->getRepository(\App\Entities\Member::class)
    );
    }

    View Slide

  82. 2. ActiveRecord vs DataMapper

    use App\Entities\Member;
    interface UowServiceInterface
    {
    public function createMembers(Member $entity): void;
    public function updateMembers(Member $entity): void;
    public function deleteMembers(Member $entity): void;
    public function commit(): void;
    }
    Interface
    class UowService implements UowServiceInterface
    {
    public function __construct(
    private readonly MemberRepositoryInterface $memberRepository
    ) { }
    public function createMembers(Member $entity): void
    {
    $this->memberRepository->add($entity);
    }
    public function updateMembers(Member $entity): void
    {
    $this->memberRepository->update($entity);
    }
    public function deleteMembers(Member $entity): void
    {
    $this->memberRepository->remove($entity);
    }
    public function commit(): void
    {
    // 永続化
    $this->memberRepository->save();
    }
    }
    具象
    3. Unit of Workパターン サンプル

    リポジトリに処理を委譲できる


    View Slide

  83. 2. ActiveRecord vs DataMapper

    Doctrineでのリファクタ後

    ● 更新するためのデータを溜め込んで渡さなく
    てよくなった

    ○ Entityは渡す必要がある

    ● Entityを渡せばよしなにやってくれる

    ○ おまかせするだけ

    ○ リレーションが増えたときも対応してく
    れる

    3. Unit of Workパターン サンプル

    class UseCase
    {
    public function __construct(
    private readonly FileInterface $file,
    private readonly UowServiceInterface $uowService,
    ) { }
    public function __invoke(FileParameterInterface $parameter)
    {
    $csv = $this->file->read($parameter->getPathName());
    foreach ($csv as $row) {
    if ($row->shouldDelete()) {
    $deleteMember = (new Member())->factory($row->toArray());
    $this->uowService->deleteMembers($deleteMember);
    continue;
    }
    if ($shouldUpdate) {
    $updateMember = (new Member())->factory($row->toArray());
    $this->uowService->updateMembers($updateMember);
    continue;
    }
    $createMember = (new Member())->factory($row->toArray());
    $this->uowService->createMembers($createMember);
    }
    $this->uowService->commit();
    }
    }

    View Slide

  84. 2. ActiveRecord vs DataMapper

    もういっちょう

    3. Unit of Workパターン サンプル


    View Slide

  85. 2. ActiveRecord vs DataMapper

    コマンドパターン

    3. Unit of Workパターン サンプル


    View Slide

  86. 2. ActiveRecord vs DataMapper

    Unit of Work パターンと

    永続性の無視 | Microsoft Learn


    https://learn.microsoft.com/ja-jp/archive/msdn-magazine/2009/june/the-unit-of-work-pattern-and-persistence-ignorance


    3. Unit of Workパターン サンプル


    View Slide

  87. 2. ActiveRecord vs DataMapper

    これのサンプルを

    PHPで書き直してみた

    (動くとは言っていない)


    3. Unit of Workパターン サンプル


    View Slide

  88. 3. Unit of Workパターン サンプル

    コマンドパターンのユースケース

    ● 請求書(Invoice)に対する処理

    ● 請求書に対して一連の処理がある

    ○ 色々なEntityに対しての処理もある

    ○ 処理は今後も増えたり減ったりするのが予想される

    ● その処理を順に実行していく


    View Slide

  89. 2. ActiveRecord vs DataMapper

    Unit of Workインターフェイス

    ● UnitOfWorkInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    interface UnitOfWorkInterface
    {
    public function markDirty($entity); // Entityを受け取るように
    public function markNew($entity);
    public function markDeleted($entity);
    public function commit();
    public function rollback();
    }
    3. Unit of Workパターン サンプル


    View Slide

  90. 2. ActiveRecord vs DataMapper

    Unit of Workインターフェイス

    ● UnitOfWorkInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    interface UnitOfWorkInterface
    {
    public function markDirty($entity); // Entityを受け取るように
    public function markNew($entity);
    public function markDeleted($entity);
    public function commit();
    public function rollback();
    }
    3. Unit of Workパターン サンプル


    View Slide

  91. 2. ActiveRecord vs DataMapper

    Unit of Work具象

    ● UnitOfWork

    4. Unit of Workパターンが解決すること

    namespace App\Example\Service;
    use App\Example\UseCase\UnitOfWorkInterface;
    use Doctrine\ORM\EntityManagerInterface;
    class UnitOfWork implements UnitOfWorkInterface
    {
    public function __construct(
    private readonly EntityManagerInterface $entityManager
    ) { }
    public function markDirty($entity)
    {
    // EntityManagerはEntityの変更を自動的に監視しているから、
    // persistする必要はないけども
    $this->entityManager->persist($entity);
    }
    3. Unit of Workパターン サンプル

    public function markNew($entity)
    {
    $this->entityManager->persist($entity);
    }
    public function markDeleted($entity)
    {
    $this->entityManager->remove($entity);
    }
    public function commit()
    {
    $this->entityManager->flush();
    }
    public function rollback()
    {
    $this->entityManager->rollback();
    }
    }

    View Slide

  92. 2. ActiveRecord vs DataMapper

    コマンド処理インターフェイス

    ● InvoiceCommandInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\UnitOfWorkInterface;
    interface InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface $unitOfWork);
    }
    3. Unit of Workパターン サンプル


    View Slide

  93. 2. ActiveRecord vs DataMapper

    コマンド処理インターフェイス

    ● InvoiceCommandInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\UnitOfWorkInterface;
    interface InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface $unitOfWork);
    }
    3. Unit of Workパターン サンプル

    UnitOfWorkInterfaceを利用して
    Invoiceに対する操作を行う何か

    View Slide

  94. 2. ActiveRecord vs DataMapper

    長期の顧客に対して割引を適用するコマンド

    ● DiscountForLoyalCustomerCommand

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    use App\Example\Entities\Customer;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    /**
    * 長期の顧客に対して割引を適用する
    */
    class DiscountForLoyalCustomerCommand implements
    InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface
    $unitOfWork)
    {
    if ($this->isLoyalCustomer($invoice->getCustomer())) {
    $invoice->applyDiscount(10); // 10%の割引を適用する
    $unitOfWork->markDirty($invoice);
    }
    }
    3. Unit of Workパターン サンプル

    private function isLoyalCustomer(Customer $customer): bool
    {
    // 顧客がロイヤル(長期)顧客であるかの判定ロジックを実装
    return true;
    }
    }

    View Slide

  95. 2. ActiveRecord vs DataMapper

    長期の顧客に対して割引を適用するコマンド

    ● DiscountForLoyalCustomerCommand

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    use App\Example\Entities\Customer;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    /**
    * 長期の顧客に対して割引を適用する
    */
    class DiscountForLoyalCustomerCommand implements
    InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface
    $unitOfWork)
    {
    if ($this->isLoyalCustomer($invoice->getCustomer())) {
    $invoice->applyDiscount(10); // 10%の割引を適用する
    $unitOfWork->markDirty($invoice);
    }
    }
    3. Unit of Workパターン サンプル

    private function isLoyalCustomer(Customer $customer): bool
    {
    // 顧客がロイヤル(長期)顧客であるかの判定ロジックを実装
    return true;
    }
    }
    Entityの変更を監視


    View Slide

  96. 2. ActiveRecord vs DataMapper

    請求書が遅延している場合にアラートを作成するコマンド

    ● LateInvoiceAlertCommand

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    // use 略;
    /**
    * 請求書(Invoice)が遅延している場合にアラートを作成する
    */
    class LateInvoiceAlertCommand implements
    InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface
    $unitOfWork)
    {
    // 遅延が無ければ何もしない
    if (!$this->isTheInvoiceLate($invoice)) {
    return;
    }
    $alert = $this->createLateAlertFor($invoice);
    $unitOfWork->markNew($alert); // 遅延アラートEntityを保存
    }
    3. Unit of Workパターン サンプル

    private function isTheInvoiceLate(Invoice $invoice): bool
    {
    // ここにinvoiceが遅延しているかどうかを判定するロジックを実装
    return true;
    }
    private function createLateAlertFor(Invoice $invoice):
    PaymentDelayAlert
    {
    // ここに遅延アラートEntityを作成するロジックを実装
    return new PaymentDelayAlert();
    }
    }

    View Slide

  97. 2. ActiveRecord vs DataMapper

    請求書が遅延している場合にアラートを作成するコマンド

    ● LateInvoiceAlertCommand

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase\Command;
    // use 略;
    /**
    * 請求書(Invoice)が遅延している場合にアラートを作成する
    */
    class LateInvoiceAlertCommand implements
    InvoiceCommandInterface
    {
    public function execute(Invoice $invoice, UnitOfWorkInterface
    $unitOfWork)
    {
    // 遅延が無ければ何もしない
    if (!$this->isTheInvoiceLate($invoice)) {
    return;
    }
    $alert = $this->createLateAlertFor($invoice);
    $unitOfWork->markNew($alert); // 遅延アラートEntityを保存
    }
    3. Unit of Workパターン サンプル

    private function isTheInvoiceLate(Invoice $invoice): bool
    {
    // ここにinvoiceが遅延しているかどうかを判定するロジックを実装
    return true;
    }
    private function createLateAlertFor(Invoice $invoice):
    PaymentDelayAlert
    {
    // ここに遅延アラートEntityを作成するロジックを実装
    return new PaymentDelayAlert();
    }
    }
    Entityの追加を監視


    View Slide

  98. 2. ActiveRecord vs DataMapper

    一連処理インターフェイス

    ● InvoiceCommandProcessorInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    interface InvoiceCommandProcessorInterface
    {
    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands);
    }
    3. Unit of Workパターン サンプル


    View Slide

  99. 2. ActiveRecord vs DataMapper

    一連処理インターフェイス

    ● InvoiceCommandProcessorInterface

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    interface InvoiceCommandProcessorInterface
    {
    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands);
    }
    3. Unit of Workパターン サンプル

    Invoiceに対する操作(コマンド)を持つ
    ものたちを受け取り、順にそのコマン
    ドを実行させる何か


    View Slide

  100. 2. ActiveRecord vs DataMapper

    一連処理の実行

    ● InvoiceCommandProcessor

    4. Unit of Workパターンが解決すること

    namespace App\Example\Service;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\InvoiceCommandProcessorInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    use Exception;
    class InvoiceCommandProcessor implements
    InvoiceCommandProcessorInterface
    {
    public function __construct(
    private readonly UnitOfWorkInterface $unitOfWork
    ) { }
    3. Unit of Workパターン サンプル

    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands)
    {
    try {
    foreach ($commands as $command) {
    $command->execute($invoice, $this->unitOfWork);
    }
    $this->unitOfWork->commit();
    } catch (Exception $e) {
    $this->unitOfWork->rollback();
    throw $e;
    }
    }
    }

    View Slide

  101. 2. ActiveRecord vs DataMapper

    一連処理の実行

    ● InvoiceCommandProcessor

    4. Unit of Workパターンが解決すること

    namespace App\Example\Service;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\InvoiceCommandProcessorInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    use Exception;
    class InvoiceCommandProcessor implements
    InvoiceCommandProcessorInterface
    {
    public function __construct(
    private readonly UnitOfWorkInterface $unitOfWork
    ) { }
    3. Unit of Workパターン サンプル

    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands)
    {
    try {
    foreach ($commands as $command) {
    $command->execute($invoice, $this->unitOfWork);
    }
    $this->unitOfWork->commit();
    } catch (Exception $e) {
    $this->unitOfWork->rollback();
    throw $e;
    }
    }
    }
    ここでコマンドが呼ばれて
    Entityが諸々監視されて

    View Slide

  102. 2. ActiveRecord vs DataMapper

    一連処理の実行

    ● InvoiceCommandProcessor

    4. Unit of Workパターンが解決すること

    namespace App\Example\Service;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\InvoiceCommandProcessorInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    use Exception;
    class InvoiceCommandProcessor implements
    InvoiceCommandProcessorInterface
    {
    public function __construct(
    private readonly UnitOfWorkInterface $unitOfWork
    ) { }
    3. Unit of Workパターン サンプル

    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands)
    {
    try {
    foreach ($commands as $command) {
    $command->execute($invoice, $this->unitOfWork);
    }
    $this->unitOfWork->commit();
    } catch (Exception $e) {
    $this->unitOfWork->rollback();
    throw $e;
    }
    }
    }
    ここでまとめてドーン

    View Slide

  103. 2. ActiveRecord vs DataMapper

    一連処理の実行

    ● InvoiceCommandProcessor

    4. Unit of Workパターンが解決すること

    namespace App\Example\Service;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\InvoiceCommandInterface;
    use App\Example\UseCase\InvoiceCommandProcessorInterface;
    use App\Example\UseCase\UnitOfWorkInterface;
    use Exception;
    class InvoiceCommandProcessor implements
    InvoiceCommandProcessorInterface
    {
    public function __construct(
    private readonly UnitOfWorkInterface $unitOfWork
    ) { }
    3. Unit of Workパターン サンプル

    /**
    * @param Invoice $invoice
    * @param InvoiceCommandInterface[] $commands
    */
    public function runCommands(Invoice $invoice, array $commands)
    {
    try {
    foreach ($commands as $command) {
    $command->execute($invoice, $this->unitOfWork);
    }
    $this->unitOfWork->commit();
    } catch (Exception $e) {
    $this->unitOfWork->rollback();
    throw $e;
    }
    }
    }
    監視されていたEntityが、こ
    こでよしなに永続化される

    View Slide

  104. 2. ActiveRecord vs DataMapper

    ユースケース

    ● InvoiceUsecase

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\DiscountForLoyalCustomerCommand;
    use App\Example\UseCase\Command\LateInvoiceAlertCommand;
    class InvoiceUseCase
    {
    public function __construct(
    private readonly InvoiceCommandProcessorInterface $invoiceCommandProcessor
    ) { }
    public function handle(Invoice $invoice)
    {
    $commands = [
    new DiscountForLoyalCustomerCommand(),
    new LateInvoiceAlertCommand()
    ];
    $this->invoiceCommandProcessor->runCommands($invoice, $commands);
    }
    }
    3. Unit of Workパターン サンプル


    View Slide

  105. 2. ActiveRecord vs DataMapper

    ユースケース

    ● InvoiceUsecase

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\DiscountForLoyalCustomerCommand;
    use App\Example\UseCase\Command\LateInvoiceAlertCommand;
    class InvoiceUseCase
    {
    public function __construct(
    private readonly InvoiceCommandProcessorInterface $invoiceCommandProcessor
    ) { }
    public function handle(Invoice $invoice)
    {
    $commands = [
    new DiscountForLoyalCustomerCommand(),
    new LateInvoiceAlertCommand()
    ];
    $this->invoiceCommandProcessor->runCommands($invoice, $commands);
    }
    }
    3. Unit of Workパターン サンプル

    Invoiceに対する処理が増えても、コマ
    ンド処理インターフェイスを満たすも
    のを渡せばいいだけ


    View Slide

  106. 2. ActiveRecord vs DataMapper

    ユースケース

    ● InvoiceUsecase

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\DiscountForLoyalCustomerCommand;
    use App\Example\UseCase\Command\LateInvoiceAlertCommand;
    class InvoiceUseCase
    {
    public function __construct(
    private readonly InvoiceCommandProcessorInterface $invoiceCommandProcessor
    ) { }
    public function handle(Invoice $invoice)
    {
    $commands = [
    new DiscountForLoyalCustomerCommand(),
    new LateInvoiceAlertCommand()
    ];
    $this->invoiceCommandProcessor->runCommands($invoice, $commands);
    }
    }
    3. Unit of Workパターン サンプル

    拡張に対して開かれている

    (Open-Closed Principle: 開放閉鎖の
    原則)


    View Slide

  107. 2. ActiveRecord vs DataMapper

    ユースケース

    ● InvoiceUsecase

    4. Unit of Workパターンが解決すること

    namespace App\Example\UseCase;
    use App\Example\Entities\Invoice;
    use App\Example\UseCase\Command\DiscountForLoyalCustomerCommand;
    use App\Example\UseCase\Command\LateInvoiceAlertCommand;
    class InvoiceUseCase
    {
    public function __construct(
    private readonly InvoiceCommandProcessorInterface $invoiceCommandProcessor
    ) { }
    public function handle(Invoice $invoice)
    {
    $commands = [
    new DiscountForLoyalCustomerCommand(),
    new LateInvoiceAlertCommand()
    ];
    $this->invoiceCommandProcessor->runCommands($invoice, $commands);
    }
    }
    3. Unit of Workパターン サンプル

    Invoiceに対する処理を色々やってく
    れているんだろうなぁという抽象具
    合。

    気づけばいい感じに永続化されてい
    る。


    View Slide

  108. 2. ActiveRecord vs DataMapper

    なんかめっちゃいい気がする

    3. Unit of Workパターン サンプル


    View Slide

  109. 3. Unit of Workパターン サンプル

    クラス図


    View Slide

  110. 3. Unit of Workパターン サンプル

    クラス図



    https://github.com/smeghead/php-class-diagram

    View Slide

  111. 2. ActiveRecord vs DataMapper

    なんかめっちゃいい気がする

    3. Unit of Workパターン サンプル


    View Slide

  112. 2. ActiveRecord vs DataMapper

    こういうの書けるようになりたい

    3. Unit of Workパターン サンプル


    View Slide

  113. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Workパターン サンプル

    4. 所感

    5. まとめ

    4. 所感


    View Slide

  114. 2. ActiveRecord vs DataMapper

    Unit of Workパターンが解決すること

    ● データベースとのやり取りを効率的に行ってくれる

    ● EntityはDBのことを知らないのでビジネスロジックに集中しやすい

    ● サービス層などでUnit of Workを使って永続化処理を隠蔽

    4. 所感


    View Slide

  115. 2. ActiveRecord vs DataMapper

    Unit of Workパターンが解決すること

    ● データベースとのやり取りを効率的に行ってくれる

    ○ データベースへの操作(挿入、更新、削除)を一括で管理してくれる

    ○ `かたまり`をきちんと見てくれる

    ■ 集約みたいな概念をきちんとやっているところと相性が良さそう

    ■ 適宜、flush(), clear() して、結果整合性に倒すとか

    ● clear()は監視しているEntityを開放する

    ■ DDD風味がある

    ○ flush() のときにしかトランザクションは発生しない

    ■ エンティティマネージャからDBコネクションを取れるのでbeginTransaction, rollback,
    commit は書ける

    4. 所感


    View Slide

  116. 2. ActiveRecord vs DataMapper

    Unit of Workパターンが解決すること

    ● EntityはDBのことを知らないのでビジネスロジックに集中しやすい

    ○ 会話したらうっかりDB更新しちゃったとかがない

    ○ 実際のDB操作はエンティティマネージャ、リポジトリにお任せ

    ○ きっちりと分かれているのでリポジトリパターンと相性が合うのが分かる

    4. 所感


    View Slide

  117. 2. ActiveRecord vs DataMapper

    Unit of Workパターンが解決すること

    ● サービス層などでUnit of Workを使って永続化処理を隠蔽

    ○ 基本的には、Entity取得, 作成, 更新して、flush()

    ○ コマンドパターンおすすめ

    4. 所感


    View Slide

  118. 2. ActiveRecord vs DataMapper

    逆に足かせになりそうなこと

    ● データアクセスする際は必ずエンティティマネージャが必要なので、手間がかかる

    ○ その分、制約を設けられる

    ○ 例えば deptrac 使ったり

    ● お任せできると言っても、Entityに書くアノテーション(マッピング)による

    ○ やはりそのへんの学習コストはある

    ● その点、ActiveRecordは気軽にアクセスできる

    ○ リーン、早く進めたいならActiveRecord

    ○ かっちりやりたいならDataMapper

    4. 所感


    View Slide

  119. 2. ActiveRecord vs DataMapper

    気になりポイント

    ● 複数の変更を効率的にデータベースに反映するとあったので、たくさんのレコードを作る際はバ
    ルク処理もしてくれるのかなぁと思ってログを見たけどそうでもなかった

    ○ 例えば、insert用にpersistを100回してから、flush

    ○ きっちり1行1行insert文を発行していた

    ● 監視対象のEntityが多すぎてもメモリを食うらしい(そりゃそうか)

    ● 対処方法はこの記事あたりが参考になるかも

    ○ DoctrineとEloquent比較大全14: バッチ更新

    ■ https://qiita.com/77web@github/items/bbdac4c1a373577cbda2

    ○ DoctrineとEloquent比較大全15: バッチ削除

    ■ https://qiita.com/77web@github/items/57d1129acddf10909c4a

    4. Unit of Workパターンが解決すること


    View Slide

  120. 2. ActiveRecord vs DataMapper

    気になりポイント

    ● 複数の変更を効率的にデータベースに反映するとあったので、たくさんのレコードを作る際はバ
    ルク処理もしてくれるのかなぁと思ってログを見たけどそうでもなかった

    ○ 例えば、insert用にpersistを100回してから、flush

    ○ きっちり1行1行insert文を発行していた

    ● 監視対象のEntityが多すぎてもメモリを食うらしい(そりゃそうか)

    ● 対処方法はこの記事あたりが参考になるかも

    ○ DoctrineとEloquent比較大全14: バッチ更新

    ■ https://qiita.com/77web@github/items/bbdac4c1a373577cbda2

    ○ DoctrineとEloquent比較大全15: バッチ削除

    ■ https://qiita.com/77web@github/items/57d1129acddf10909c4a

    4. Unit of Workパターンが解決すること


    PHPのORM: DoctrineORMとEloquentの比較大全をひとりでやる

    Advent Calendar 2022


    View Slide

  121. 2. ActiveRecord vs DataMapper

    気になりポイント

    ● 大量データの扱いに関して少し調べてみると、やはりそこは生のクエリを書くか、バッチ処理に
    するか、キューなどを使うかなどであった

    ● それはそれで別のソリューションを考える必要がある

    ● いきつくところは結局そこなのか...という思いがある

    ● が、それをひっくるめても勉強を続けて実務で使いたいお気持ち

    4. Unit of Workパターンが解決すること


    View Slide

  122. 2. ActiveRecord vs DataMapper

    TIPS

    ● ログの吐かせ方

    ○ `src/vendor/laravel-doctrine/orm/config/doctrine.php`を、

    `src/config/`以下に配置

    ■ 自分はなぜかpublishできなかった

    ○ Doctrine\DBAL\Logging\Middleware::class のコメントを外せばOK

    4. Unit of Workパターンが解決すること

    // src/config/doctrine.php
    'managers' => [
    // 略
    'middlewares' => [
    Doctrine\DBAL\Logging\Middleware::class // コメントを外す
    ]
    ]

    View Slide

  123. 2. ActiveRecord vs DataMapper

    TIPS

    ● デバッグ

    ○ `APP_DEBUG` を true に変える



    ○ `ddd()`とかで見れる

    4. Unit of Workパターンが解決すること

    'managers' => [
    'default' => [
    'dev' => env('APP_DEBUG', true), // trueに

    View Slide

  124. 2. ActiveRecord vs DataMapper

    TIPS

    ● Entityの名前空間

    ○ デフォルトは、`App\Entities`

    ○ 変更したければconfigを修正





    ○ 他にも、`repository`、`DOCTRINE_METADATA`とか変えられる

    4. Unit of Workパターンが解決すること

    'managers' => [
    'default' => [
    // 〜
    'paths' => [
    base_path('app/Entities') // ここ変える
    ],

    View Slide

  125. 2. ActiveRecord vs DataMapper

    余談

    ● Unit of WorkパターンをLaravelにもどうだ?というプロポーザルが挙げられていた

    ○ 2017/03 頃

    ● https://github.com/laravel/ideas/issues/495

    4. Unit of Workパターンが解決すること


    View Slide

  126. 2. ActiveRecord vs DataMapper

    余談

    ● Unit of WorkパターンをLaravelにもどうだ?というプロポーザルが挙げられていた

    ○ 2017/03 頃

    ● https://github.com/laravel/ideas/issues/495

    4. Unit of Workパターンが解決すること


    View Slide

  127. 1. Unit of Workパターンとは

    2. ActiveRecord vs DataMapper

    3. Unit of Work パターンサンプル

    4. メリット / デメリット

    5. まとめ

    5. まとめ


    View Slide

  128. 2. ActiveRecord vs DataMapper

    Doctrineが Unit of Work でした

    4. メリット / デメリット

    5. まとめ


    View Slide

  129. 2. ActiveRecord vs DataMapper

    永続化、トランザクションは

    エンティティマネージャが

    よしなにやってくれます

    4. メリット / デメリット

    5. まとめ


    View Slide

  130. 2. ActiveRecord vs DataMapper

    Unit of Workを使って

    永続化の処理をうまく隠蔽して

    書こうと思えば書けます

    4. メリット / デメリット

    5. まとめ


    View Slide

  131. 2. ActiveRecord vs DataMapper

    Doctrine(DataMapper)

    使ってみよう

    4. メリット / デメリット

    5. まとめ


    View Slide

  132. 2. ActiveRecord vs DataMapper

    自ずとUnit of Workが分かります

    (きっと)

    4. メリット / デメリット

    5. まとめ


    View Slide

  133. 1. Unit of Workパターンとは

    参考


    ● Patterns of Enterprise Application Architecture

    ○ https://www.amazon.co.jp/-/en/Martin-Fowler/dp/0321127420

    ● ちょうぜつ Advent Calendar 2022 6日目

    ○ https://qiita.com/tanakahisateru/items/b0c441c4540e84fe6dea

    ● P of EAA: Unit of Work

    ○ https://martinfowler.com/eaaCatalog/unitOfWork.html

    ● Unit of Work パターンと永続性の無視 | Microsoft Learn

    ○ https://learn.microsoft.com/ja-jp/archive/msdn-magazine/2009/june/the-unit-of-work
    -pattern-and-persistence-ignorance

    5. まとめ


    View Slide

  134. 1. Unit of Workパターンとは

    参考


    ● PHPのORM: DoctrineORMとEloquentの比較大全をひとりでやるAdvent
    Calendar 2022

    ○ https://qiita.com/advent-calendar/2022/php-doctrine-orm-vs-eloquent

    ● How is Doctrine 2 different to Eloquent? | Culttt

    ○ https://culttt.com/2014/07/07/doctrine-2-different-eloquent

    ● GoによるRepositoryパターンとUnit of Workを組み合わせたトランザクショ
    ン処理

    ○ https://zenn.dev/hacobell_dev/articles/0ae114500cf974

    ○ Unit of Work に興味を持つきっかけになった記事

    ○ Goで調べ物をしていたときに見かけた

    5. まとめ


    View Slide

  135. 2. ActiveRecord vs DataMapper

    ご清聴ありがとうございました

    4. メリット / デメリット

    5. まとめ


    View Slide