Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPではじめるCQRSっぽいやつ

男爵
March 27, 2021

 PHPではじめるCQRSっぽいやつ

PHPerKaigi2021のアンカンファレンスで使ったものです。
PHPカンファレンス仙台2019の再演です。

男爵

March 27, 2021
Tweet

More Decks by 男爵

Other Decks in Programming

Transcript

  1. 自己紹介 ✘ HN:男爵 ✘ dnskimo ✘ dnskimox ✘ ソフトウェアエンジニア ✘

    北海道在住 ✘ アルプ株式会社 ✘ サブスク管理SaaS開発
  2. バートランド・メイヤーのコマンドクエリ分離原 則(CQS) ✘ クラス設計の原則 ✘ あらゆるクラスの特性はコマンド (命令)とクエリ(問い合わせ)に分 けられる ✘ 両者を明確に区別することで、単

    純で読みやすいソフトウェアを作り 出し、信頼性、再利用性、拡張性 を飛躍的に向上させることができ る https://en.wikipedia.org/wiki/Bertrand_Meyer
  3. <?php $character = new Character(...); $character->getLevel(); // 1 if ($character->canLevelUp())

    { echo “This character can level up!”; } $character->getLevel(); // 2 いつの間にか回答が変化している! クエリ? クエリ?
  4. <?php class Character { private $level = 1; private $exp

    = 0; // コマンドの例 public function gainExp(int $exp): void { assert($exp > 0); $this->exp += $exp; // 経験値100毎にレベルアップ while ($this->level < floor($this->exp / 100) + 1) { $this->level++; } } }
  5. <?php class Character … private $level = 1; private $exp

    = 0; // クエリの例 public function getLevel(): int { return $this->level; } // クエリの例 public function getExpForNextLevel(): int { return 100 - $this->exp % 100; } }
  6. <?php $character = new Character(...); $before_level = $character->getLevel(); $character->gainExp(100); if

    ($before_level < $character->getLevel()) { printf(“Level up! Next exp is %d”, $character->getExpForNextLevel()); } クエリ クエリ コマンド
  7. グレッグ・ヤングのコマンドクエリ責務分離 (CQRS) ✘ コマンドクエリ分離原則に基づい たアーキテクチャパターン ✘ システムのユースケースはコマ ンド(更新)とクエリ(参照)に分類 できる ✘

    コマンドとクエリにはそれぞれ非 常に異なるニーズがあるので分 離すべき https://www.developerfusion.com/event/153843/special-guest-greg-young-on-tue-aug-13th/
  8. <?php class GainExpService { public function execute(...) { $character =

    Character::findById(123); $character->exp += 100; while ($character->level < floor($character>exp / 100) + 1) { $character>level++; } } }
  9. <?php /** * @property int $character_id * @property string $name

    * @property int $friend_count */ class CharacterSummary extends QueryModel { protected static $properties = [ 'character_id' => ['type' => 'type'], 'name' => ['type' => 'string'], 'friend_count' => ['type' => 'int'], ]; }
  10. <?php class CharacterSummary... public static function findById(int $character_id): self {

    $result = QueryBuilder::table('character_tbl') ->select('character_tbl.character_id', 'name', 'COUNT(friend_id) AS friend_count') ->leftJoin('friend_tbl', 'USING', 'character_id') ->where('character_tbl.character_id', $character_id) ->group_by('character_id') ->get_one(); return self::inflate($result); } }
  11. <?php class CharacterId { private $character_id; public function __construct(int $character_id)

    { $this->character_id = $character_id; } public function formatted(): string { return sprintf("P%05d", $this->character_id); } }
  12. 参考資料 ✘ Greg Young流CQRSの和訳版 ✘ Greg Young流CQRS - Mark Nijhof

    ✘ 副作用を最小限に抑えるために必要なこと ✘ オブジェクト指向入門 第2版 方法論・実践 ✘ Patterns of Enterprise Application Architecture ✘ 達人に学ぶSQL徹底指南書
 ✘ Domain Model- P of EAA Catalog ✘ Transaction Script - P of EAA Catalog ✘ ValueObject - Martin Fowler