<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
  <title>Shin x Blog</title>
  
  <subtitle>PHPをメインにWebシステムを開発してます。Webシステム開発チームの技術サポートも行っています。</subtitle>
  
  <link href="https://blog.shin1x1.com/"/>
  <updated>2020-01-22T10:00:00+09:00</updated>
  <author>
    <name>shin1x1</name>
  </author>
  <generator uri="https://blog.hatena.ne.jp/" version="3ca5fde67a39279166b96fa03e3ad61f">Hatena::Blog</generator>
  <id>hatenablog://blog/12921228815721754520</id>

  
    
    
    <entry>
        <title>誰のためのソースコード?</title>
        <link href="https://blog.shin1x1.com/entry/the-psychology-of-everyday-codes"/>
        <id>hatenablog://entry/26006613497254376</id>
        <published>2020-01-22T10:00:00+09:00</published>
        <updated>2020-01-22T10:00:02+09:00</updated>        <summary type="html">「誰のためのデザイン?」の旧版と改訂・増補版を読みました。 以前に旧版を読んだのですが、その記憶もあやふやなくらい前だったので、あらためて読み直し、その面白さゆえに改訂・増補版も購入してこれも読み終えました。 Web システム開発を生業としているので、日々ユーザとのインタラクションが発生するプロダクト開発に関わっているわけで、ユーザがどのように製品を認知し、使うかという内容は参考になりました。 それとは別にソフトウェア開発という観点で、ソースコードを読む時に人がどのように認知するのかという点でも興味深いものでした。このエントリでは、自分なりに整理したソースコードと概念モデルについて残しておきま…</summary>
        <content type="html">&lt;p&gt;「誰のためのデザイン?」の旧版と改訂・増補版を読みました。&lt;/p&gt;

&lt;p&gt;以前に旧版を読んだのですが、その記憶もあやふやなくらい前だったので、あらためて読み直し、その面白さゆえに改訂・増補版も購入してこれも読み終えました。&lt;/p&gt;

&lt;p&gt;Web システム開発を生業としているので、日々ユーザとのインタラクションが発生するプロダクト開発に関わっているわけで、ユーザがどのように製品を認知し、使うかという内容は参考になりました。&lt;/p&gt;

&lt;p&gt;それとは別にソフトウェア開発という観点で、ソースコードを読む時に人がどのように認知するのかという点でも興味深いものでした。このエントリでは、自分なりに整理したソースコードと概念モデルについて残しておきます。&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#ソースコードとメンタルモデル&quot;&gt;ソースコードとメンタルモデル&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#ソースコードの概念モデル&quot;&gt;ソースコードの概念モデル&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#実行モデル&quot;&gt;実行モデル&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#プログラマモデル&quot;&gt;プログラマモデル&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#ユーザモデル&quot;&gt;ユーザモデル&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#誰のためのソースコード&quot;&gt;誰のためのソースコード?&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#メモ&quot;&gt;メモ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;ソースコードとメンタルモデル&quot;&gt;ソースコードとメンタルモデル&lt;/h2&gt;

&lt;p&gt;ある程度、経験のあるプログラマであれば、ソースコードを見るだけで想起されるイメージがあります。例えば、for を見れば繰り返しを思い浮かべるでしょうし、if を見れば条件分岐を思い浮かべるでしょう。これは、読み手自身にコードを理解するメンタルモデルが存在するからです。&lt;/p&gt;

&lt;p&gt;for や if を文と見るか、式と見るかは書かれたプログラミング言語が何か、またそのメンタルモデルを持つかどうかによります。Scala のように for に繰り返し以外の動作がある言語もあります。こうしたものは、前提となる知識を理解しておかないと読む手は理解できません。&lt;/p&gt;

&lt;pre class=&quot;code lang-scala&quot; data-lang=&quot;scala&quot; data-unlink&gt;&lt;span class=&quot;synType&quot;&gt;object&lt;/span&gt; Sample &lt;span class=&quot;synType&quot;&gt;extends&lt;/span&gt; App {
  &lt;span class=&quot;synType&quot;&gt;val&lt;/span&gt; s = &lt;span class=&quot;synStatement&quot;&gt;for&lt;/span&gt; {
    i &amp;lt;- Some(&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;)
    j &amp;lt;- Some(&lt;span class=&quot;synConstant&quot;&gt;2&lt;/span&gt;)
  } &lt;span class=&quot;synType&quot;&gt;yield&lt;/span&gt; (i + j)

  println(s) &lt;span class=&quot;synComment&quot;&gt;// Some(3)&lt;/span&gt;
}
&lt;/pre&gt;


&lt;p&gt;デザインパターンを利用したコードは、その概念モデルを理解している読み手に対してはコードの理解を助けます。しかし、そうではない場合、ただ冗長で理解しづらいコードのように映るかもしれません。&lt;/p&gt;

&lt;p&gt;つまり、ソースコードを理解するには、コードの概念モデルと読み手のメンタルモデルが一致する必要があります。&lt;/p&gt;

&lt;h2 id=&quot;ソースコードの概念モデル&quot;&gt;ソースコードの概念モデル&lt;/h2&gt;

&lt;p&gt;ソースコードには書き手の概念モデルが反映されます。この概念モデルと読み手のメンタルモデルが一致する、もしくは読み手が理解できれば、ソースコードの内容を把握して利用できます。読み手はソースコードの概念モデルを理解することが重要です。&lt;/p&gt;

&lt;p&gt;ソースコードが持つ概念モデルは「実行モデル」「プログラマモデル」「ユーザモデル」の3つに分けられると考えました。&lt;/p&gt;

&lt;h3 id=&quot;実行モデル&quot;&gt;実行モデル&lt;/h3&gt;

&lt;p&gt;実行モデルは、コンピュータがソースコードを解釈し、実行する（実行可能な状態にする）ためのモデルです。多くの場合、コンパイラやランタイムの概念モデルといえます。これ理解できなければ、コードがどのように動作するのかをイメージできません。このモデルは一般的なもので、プログラミング言語のマニュアルや技術書などで広く公開されています。同じ言語やプラットフォーム、フレームワークなどでは同じモデルとなるので、一度学習すれば他のソフトウェアでも流用できます。また、実際に実行したり、自動テストを実行するなどで検証が比較的容易です。&lt;/p&gt;

&lt;h3 id=&quot;プログラマモデル&quot;&gt;プログラマモデル&lt;/h3&gt;

&lt;p&gt;プログラマモデルは、プログラマがソースコードを理解するためのモデルです。分かりやすい変数名を付ける、関数やクラス構造に分ける、デザインパターンを適用する、レイヤ分けなどのアーキテクチャを構成するなどです。いわゆる可読性、保守性の高いコードを書くためのモデルです。これは、読み手のメンタルモデルによって、理解しやすいかは変わってきます。我々は、書籍「リーダブルコード」や「Effective XXX」のような情報で共通認識を持っているので、それを理解している人であればそれに従うことで同じモデルを利用できます。しかし、そうではないメンタルモデルを持つチームでは、また異なるモデルをコードで表現する必要があります。&lt;/p&gt;

&lt;h3 id=&quot;ユーザモデル&quot;&gt;ユーザモデル&lt;/h3&gt;

&lt;p&gt;ユーザモデルは、ソフトウェアを利用して実現したい機能や対象とするドメインなどを示すモデルです。要求モデルやドメインモデルといったものです。重要なモデルですが、ソースコード自体にこのモデルが反映されているかは実装次第です。ソースコードのみから読み取るのが難しい場合も多く、ドキュメントや口頭による説明など他の手段で共有します。&lt;/p&gt;

&lt;p&gt;ユーザモデルは問題領域のモデル、実行モデルとプログラマモデルは解決領域のモデルともいえます。&lt;/p&gt;

&lt;h2 id=&quot;誰のためのソースコード&quot;&gt;誰のためのソースコード?&lt;/h2&gt;

&lt;p&gt;ソースコードを理解するには、ソースコードと読み手のメンタルモデルが必要になります。よく「可読性の高いコード」という表現をしますが、可読性を決めるのはソースコードだけではなく、読み手のメンタルモデルも大きく影響します。&lt;/p&gt;

&lt;p&gt;これは大切な視点で、特にプロダクションコードであれば、同じチーム外の人が理解する必要はなく、一般的にはリーダブルコードで無くても、その特有の概念モデルが共有できれていれば良いという考え方もできます。もちろん、後から入る人の事も考えて、一般的な書き方にしておく方が得策です。しかし、すでにそうではないコードがある場合、コードを直す以外にチームへの概念モデル共有をしっかりやるという方法も考えられるということは一つの考え方として認識しておくと良いでしょう。&lt;/p&gt;

&lt;p&gt;デザインパターンや 戦術的 DDD パターンが良いと聞いて、自分たちのソフトウェアに反映しようとしても中々うまくいかない、自分は理解できるがメンバーには理解されないということもパターンの概念モデルと各自が持つメンタルモデルの齟齬によって起こると言えます。&lt;/p&gt;

&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;ソースコードは、コンピュータをどう動かすかということを示すものですが、これを書くのも読むのも人です（今のところ）。つまり、人がソースコードをどう理解するのかという視点はソフトウェア開発の現場では重要なことです。自分の中で課題として考えていたことが、こうした視点で本書を読むことで、ある程度整理することができました。&lt;/p&gt;

&lt;p&gt;他にも多くのインスピレーションを与えてくれる本なので、これからも何度となく開くことになるでしょう。願わくば、いつでも読めるように電子書籍版を発売してほしいところです。&lt;/p&gt;

&lt;p&gt;人は勝手に独自のストーリーを描く生き物なので、本書を読んでも、このエントリを読んでも、あなたが持つメンタルモデルによって、また異なるイメージを描くでしょう。うまく私の概念モデルが伝わると良いのですが。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;https://www.amazon.co.jp/exec/obidos/ASIN/478850362X/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/412Z7VZFGDL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;誰のためのデザイン?―認知科学者のデザイン原論 (新曜社認知科学選書)&quot; title=&quot;誰のためのデザイン?―認知科学者のデザイン原論 (新曜社認知科学選書)&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;https://www.amazon.co.jp/exec/obidos/ASIN/478850362X/shinxblog-22/&quot;&gt;誰のためのデザイン?―認知科学者のデザイン原論 (新曜社認知科学選書)&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt;&lt;a href=&quot;http://d.hatena.ne.jp/keyword/%A5%C9%A5%CA%A5%EB%A5%C9%A1%A6A.%20%A5%CE%A1%BC%A5%DE%A5%F3&quot; class=&quot;keyword&quot;&gt;ドナルド・A. ノーマン&lt;/a&gt;,&lt;a href=&quot;http://d.hatena.ne.jp/keyword/D.A.%20%A5%CE%A1%BC%A5%DE%A5%F3&quot; class=&quot;keyword&quot;&gt;D.A. ノーマン&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 新曜社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 1990/02/01&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;https://www.amazon.co.jp/exec/obidos/ASIN/4788514346/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51uEWA1B3EL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;誰のためのデザイン？　増補・改訂版　―認知科学者のデザイン原論&quot; title=&quot;誰のためのデザイン？　増補・改訂版　―認知科学者のデザイン原論&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;https://www.amazon.co.jp/exec/obidos/ASIN/4788514346/shinxblog-22/&quot;&gt;誰のためのデザイン？　増補・改訂版　―認知科学者のデザイン原論&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt;&lt;a href=&quot;http://d.hatena.ne.jp/keyword/D.%20A.%20%A5%CE%A1%BC%A5%DE%A5%F3&quot; class=&quot;keyword&quot;&gt;D. A. ノーマン&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 新曜社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2015/04/23&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;h2 id=&quot;メモ&quot;&gt;メモ&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;学習された無力感、教えられた無力感&lt;/li&gt;
&lt;li&gt;行動遂行のサイクル

&lt;ul&gt;
&lt;li&gt;ゴール、意図の形成&lt;/li&gt;
&lt;li&gt;行為の詳細化&lt;/li&gt;
&lt;li&gt;行為の実行&lt;/li&gt;
&lt;li&gt;外界の状況の知覚&lt;/li&gt;
&lt;li&gt;外界の状況の解釈&lt;/li&gt;
&lt;li&gt;結果の評価&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ポジティブな情動、ネガティブな情動&lt;/li&gt;
&lt;li&gt;ヒューマンエラーではなくデザインエラー&lt;/li&gt;
&lt;li&gt;ポジティブ心理学&lt;/li&gt;
&lt;li&gt;短期記憶、長期記憶、外部記憶

&lt;ul&gt;
&lt;li&gt;短期記憶=スタック、長期記憶=ヒープ、外部記憶=ストレージに例えられる&lt;/li&gt;
&lt;li&gt;[旧版] 三目並べのデシジョンツリー。浅いか or 狭いものが分かりやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;デザインにおいて成功の秘訣は、何が本当の問題かを理解すること&lt;/li&gt;
&lt;li&gt;漸進的なイノベーションは毎年何百万も起こるが、急進的なイノベーションは遥かに少ない。

&lt;ul&gt;
&lt;li&gt;急進的なイノベーションを求めがちだがそれには時間がかかる。漸進的なイノベーションを積み重ねるのも大切。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;認知と情動。本能レベル、行動レベル、内省レベル

&lt;ul&gt;
&lt;li&gt;例: ブランドイメージや利用経験によるプラスの内省的な情動により、使いにくさや悪い経験といったマイナスの情動を覆す&lt;/li&gt;
&lt;li&gt;スパゲッティコードを見ると、本能レベル or 行動レベルでマイナスの情動を引き起こす？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;エラー

&lt;ul&gt;
&lt;li&gt;スリップとミステイク&lt;/li&gt;
&lt;li&gt;スリップの型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;高次の記述と低次の詳細記述&lt;/li&gt;
&lt;li&gt;強制選択機能&lt;/li&gt;
&lt;li&gt;スイスチーズメタファー&lt;/li&gt;
&lt;li&gt;人間中心デザイン（HCD）

&lt;ul&gt;
&lt;li&gt;反復サイクルは、アジャイルやリーンスタートアップと同様（本文にも書かれている）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</content>        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/412Z7VZFGDL._SL160_.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「Kubernetes で構築する PHP 開発環境」を発表しました</title>
        <link href="https://blog.shin1x1.com/entry/php-dev-on-kubernetes"/>
        <id>hatenablog://entry/26006613477176010</id>
        <published>2019-12-06T12:22:59+09:00</published>
        <updated>2019-12-06T12:22:59+09:00</updated>        <summary type="html">2019/12/01 に東京で行われた PHP Conference Japan 2019 - #phpcon にて、「Kubernetes で構築する PHP 開発環境」を発表してきました。 発表資料 サンプルコード: https://github.com/shin1x1/laravel-k8s-dev-sample Togetter: https://togetter.com/li/1438089 Kubernetes を使って、PHP 開発環境を構築するという内容でした。デモでは、Mac に構築した Kubernetes クラスタを紹介して、Mac にあるソースコードを変更すると、Kub…</summary>
        <content type="html">&lt;p&gt;2019/12/01 に東京で行われた &lt;a href=&quot;https://phpcon.php.gr.jp/2019/&quot;&gt;PHP Conference Japan 2019 - #phpcon&lt;/a&gt; にて、「Kubernetes で構築する PHP 開発環境」を発表してきました。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;a25862fe19d54b9b9dbb15b35eee1724&quot; data-ratio=&quot;1.33159947984395&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;ul&gt;
&lt;li&gt;サンプルコード: &lt;a href=&quot;https://github.com/shin1x1/laravel-k8s-dev-sample&quot;&gt;https://github.com/shin1x1/laravel-k8s-dev-sample&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Togetter: &lt;a href=&quot;https://togetter.com/li/1438089&quot;&gt;https://togetter.com/li/1438089&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Kubernetes を使って、PHP 開発環境を構築するという内容でした。デモでは、Mac に構築した Kubernetes クラスタを紹介して、Mac にあるソースコードを変更すると、Kubernetes クラスタ内で実行している PHP Pod で読み込めるということを紹介しました。PHP 開発では重要なことですね。&lt;/p&gt;

&lt;p&gt;開発環境に特化した内容なので、本番等で使うときはまた違う点がありますが、いずれ開発環境を構築する際に参考になれば嬉しいです。&lt;/p&gt;

&lt;p&gt;サンプルコードでは、Makefile を用意しているので、Kubernetes を自分の PC で動かしてみるサンプルとしても使えると思います。Docker Desktop の Kubernetes 機能を Enable にして試してみて下さい。&lt;/p&gt;

&lt;p&gt;セッション前に参加者の方に挙手でアンケートを取ったのですが、Kubernetes を使っている方はわずか 10 人程度でした。実際のところは、まだまだ使われていないんだなあということと、それにも関わるず聞きに来られた方が多くいたということでその関心の高さもうかがえました。&lt;/p&gt;

&lt;h2&gt;Kubernetes on CircleCI による CI&lt;/h2&gt;

&lt;p&gt;元々の資料には入れていたのですが、時間の関係で発表前に削除したのが、この CI に関する部分です。公開資料には追加していますので、興味がある方は資料をご覧ください。
&lt;a href=&quot;https://speakerdeck.com/shin1x1/php-development-environment-on-kubernetes?slide=26&quot;&gt;https://speakerdeck.com/shin1x1/php-development-environment-on-kubernetes?slide=26&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CircleCI + Kubernetes の組み合わせで情報を探していると、Kubernetes クラスタは、GKE や EKS など外部にあって、それを CircleCI の中から、kubectl 等で操作するというパターンは多いのですが、CircleCI の中で Kubernetes を動かすというものは少なく、色々と試行錯誤を行いました。&lt;/p&gt;

&lt;p&gt;基本は、minikube なり kind なりを入れれば良いという話なのですが、なぜか kubectl apply が失敗したり、クラスタ起動が遅かったりで、動くけど、まだまだ改善の余地があるというが今の印象です。&lt;/p&gt;

&lt;p&gt;CircleCI 側で Kubernetes 環境が用意されていて、CI 時によしなに利用できるようになると嬉しいですね。&lt;/p&gt;

&lt;p&gt;このあたり、みんながどうしているか聞いてみたいところです。&lt;/p&gt;

&lt;h2&gt;対話&lt;/h2&gt;

&lt;p&gt;自分のセッションがあるイベントでは、あまり他の方の発表は聞けないことが多いのですが、今回は&lt;a href=&quot;https://blog.shin1x1.com/entry/fortee-meets-independent-core-layer-pattern&quot;&gt;前日&lt;/a&gt;にも発表があったこともあり、セッションに参加してもいまいち集中できなかったので、うろうろしながら、色々な方と話していました。&lt;/p&gt;

&lt;p&gt;いつもはあまりいないスタッフ、スピーカー控室（和室）でゆっくりして、周りにいた方とお話ができたのも楽しかったです。場所自体がカンファレンスの喧騒からは切り離されたところなので、落ち着いて話ができるのが良かったです。&lt;/p&gt;

&lt;p&gt;本当は、もっと話したい方もいたし、これまで接点が無かった方とも話してみたかったのですが、どうにも時間も体力も足りませんでした。またの機会にお願いします！&lt;/p&gt;

&lt;p&gt;最近は、&lt;a href=&quot;https://php-genba.shin1x1.com/&quot;&gt;PHPの現場&lt;/a&gt; をやっているおかげで、配信した内容が会話のネタになったり、ゲスト出演の依頼が話しかけるきっかけにもなったりしています。何人かの方に承諾して頂けたので、来年の収録が楽しみです。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;前日は PHPerKaigi（派生イベント）、当日は PHP カンファレンスということで、両イベントのキャラクタが違いが感じられました。PHPerKaigi は、参加者同士が交流しやすくする仕掛けが多く用意されてあり、主催側がそれを促していること。一方、PHP カンファレンスは、場は用意するので、あとはご自由にという印象でした。&lt;/p&gt;

&lt;p&gt;もちろん、どちらが良い悪いという話ではなく、また規模感も参加費も何もかもが違うので単純には比較できないですが、2 日間で異なる雰囲気のイベントに登壇したので、その対比が面白かったです。&lt;/p&gt;

&lt;p&gt;毎年ながら、あの規模のイベントをつつがなく運営されているのは本当にありがたい限りです。今年も楽しいカンファレンスをありがとうございました！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20191206/20191206122005.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
    <entry>
        <title>「独立したコアレイヤパターンの適用 - fortee 編 -」を発表しました</title>
        <link href="https://blog.shin1x1.com/entry/fortee-meets-independent-core-layer-pattern"/>
        <id>hatenablog://entry/26006613475605032</id>
        <published>2019-12-06T09:19:42+09:00</published>
        <updated>2019-12-06T09:19:42+09:00</updated>        <summary type="html">2019/11/30 に東京で行われた 大改修！PHPレガシーコードビフォーアフター にて、「独立したコアレイヤパターンの適用 - fortee 編 -」を発表してきました。 発表資料 サンプルコード: https://github.com/shin1x1/independent-core-layer-pattern-fortee-demo Togetter: https://togetter.com/li/1438084 オーガナイザの @tomzoh さんが開発、運用されている fortee.jp というカンファレンス運用サービスのソースコードに独立したコアレイヤパターンを適用するという内…</summary>
        <content type="html">&lt;p&gt;2019/11/30 に東京で行われた &lt;a href=&quot;https://phperkaigi.connpass.com/event/155509/&quot;&gt;大改修！PHPレガシーコードビフォーアフター&lt;/a&gt; にて、「独立したコアレイヤパターンの適用 - fortee 編 -」を発表してきました。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;638b9e1939a741cf96364d346f00c89d&quot; data-ratio=&quot;1.33159947984395&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;ul&gt;
&lt;li&gt;サンプルコード: &lt;a href=&quot;https://github.com/shin1x1/independent-core-layer-pattern-fortee-demo&quot;&gt;https://github.com/shin1x1/independent-core-layer-pattern-fortee-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Togetter: &lt;a href=&quot;https://togetter.com/li/1438084&quot;&gt;https://togetter.com/li/1438084&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;オーガナイザの @tomzoh さんが開発、運用されている &lt;a href=&quot;https://fortee.jp/&quot;&gt;fortee.jp&lt;/a&gt; というカンファレンス運用サービスのソースコードに独立したコアレイヤパターンを適用するという内容です。&lt;/p&gt;

&lt;p&gt;最初にコアレイヤパターンについて解説をした後、ライブコーディングで実際のコードに対してパターンを適用するデモを行いました。&lt;/p&gt;

&lt;h2&gt;100 人でやるモブプロ&lt;/h2&gt;

&lt;p&gt;コードの解説をしながら、ライブコーディングを進めていくわけですが、書きながら、あれ？PhpStorm の警告が出てままになっているなと思ったら、会場から指摘して頂いて気づくということが何度かありました。（特に @hamaco さん。感謝 m(_ _)m）&lt;/p&gt;

&lt;p&gt;ライブコーディングは、コードを完成させることも大事ですが、それより、書いていく過程をみんなで共有するというのがポイントだと思います。より伝わるというのは、セッション後にいただいたフィードバックでも感じられたので、今後も機会があればやっていきたいです。&lt;/p&gt;

&lt;p&gt;当日は雰囲気が良くて、みんなに見られるというより、見守られながら、壇上でコードを書くのはとても楽しかったです。ありがとうございました。&lt;/p&gt;

&lt;p&gt;なお、引数の uuid などを ValueObject にしないのですか？という質問がありましたが、今回はコアレイヤパターンの適用が目的だったこと、また漸進的に少しづつ適用することを示したかった、そして時間が厳しかったので行いませんでした。普段なら作るので、ツッコミたくなるのはすごく分かります :)&lt;/p&gt;

&lt;h2&gt;PhpStorm&lt;/h2&gt;

&lt;p&gt;ライブコーディング中は、コードを書いて話すことに集中していたので、あまり気が付かなかったのですが、後で Twitter を見返すと、PhpStorm の操作に関するフィードバックが多かったことに驚きました。IDE を使い、さらに型を適切に付けていくことで、リズム良くコードが書けることが共有できて良かったです。&lt;/p&gt;

&lt;p&gt;昔、Vim をメインに開発していた頃、とある勉強会で Eclipse による Java のライブコーディングを見て、それがまるで魔法のようにコードができあがっていくのに感動したことがありました。それでも、IDE を使い出したのは、さらに数年後になるわけですが、そんな自分が、IDE の使い方で驚かれるというのは不思議な感覚です。ああ、気がつけば、ある程度使えるようになってるんだなあと。&lt;/p&gt;

&lt;p&gt;翌日に PHP カンファレンスがあったのですが、このデモを見て、サムライズムさん（ JetBrains 公式パートナー）のブースに足を運んだ方もいたみたいです。PhpStorm 良いツールですね。&lt;/p&gt;

&lt;p&gt;Vim 派の方が、PhpStorm に惹かれるといった意見もいくつかありました。自分も Vim から PhpStorm に移ったタイプなので&lt;a href=&quot;#f-a146f5de&quot; name=&quot;fn-a146f5de&quot; title=&quot;Vim は今も使ってます&quot;&gt;*1&lt;/a&gt;、IdeaVim （JetBrains IDE 用 Vim エミュレータプラグイン）を入れて、ぜひ PhpStorm 試してみて下さい。&lt;/p&gt;

&lt;h2&gt;独立したコアレイヤパターン&lt;/h2&gt;

&lt;p&gt;当日も、翌日の PHP カンファレンスでも、コアレイヤパターンに関する質問を受けることが何度かありました。中には実践している方もいて、その中でどう活用するか、特定のケースでどのように考えるかといったお話もありました。色々な人の考えを聞くことで、より立体的にこのパターンを見ることができ、とても勉強になりました。&lt;/p&gt;

&lt;p&gt;話題としては下記のようなものでした。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;共通するロジックやポートをどう共通化するか&lt;/li&gt;
&lt;li&gt;どの箇所に適用するか、しないか

&lt;ul&gt;
&lt;li&gt;Query と Command&lt;/li&gt;
&lt;li&gt;ビューに関する読み込み&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;実際のユースケースでの構造&lt;/li&gt;
&lt;li&gt;こうした構造のメリットをどう理解してもらうか&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;こうしたイベントでお話を聞いていると、ベースのアイデア自体は少しづつ理解して頂けることが増えた印象なのですが、いざ実践したり、実践を想定してご自身の現場への導入を考えた際にどうすれば良いのだろうという内容が出てきました。&lt;/p&gt;

&lt;p&gt;まずは、ベースのアイデアを理解してもらおうと、これまで単純なユースケースでの実装例を公開していたのですが、もう少し実践よりのコードがあってもいいかなとも考えています。&lt;/p&gt;

&lt;p&gt;また機会があれば、養殖物ではなく、天然物のコードに適用するデモができると良いですね。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;他の方のセッションでは、それぞれの視点で改善点を解説されていて、私が fortee のコードを見ていたということもありますが、そのどれもが面白く、聞きながら良いイベントだなあと嬉しくなりました。TL やアンケートを見ていても、参加された方も楽しんで頂けたようで、良かったです。&lt;/p&gt;

&lt;p&gt;公開を前提とせずに、自分が書いたコードを人前でネタにされて、あれやこれや言われるのはなかなか勇気がいることだと思います。当然、書いた状況やコンテキストがあるわけで、後で冷静に考えれば、自分でもそうは書かないということがあったりもしたと思いますが、言い訳もせずに快くコードを差し出してくれた長谷川さんに感謝です。良いイベントをありがとうございました！&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-a146f5de&quot; name=&quot;f-a146f5de&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;Vim は今も使ってます&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20191206/20191206091802.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>関数型言語で DDD - Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#</title>
        <link href="https://blog.shin1x1.com/entry/domain-modeling-made-functional"/>
        <id>hatenablog://entry/26006613381750738</id>
        <published>2019-08-20T08:30:00+09:00</published>
        <updated>2019-08-20T09:52:48+09:00</updated>        <summary type="html">オブジェクト指向言語でドメインモデルを実装することが当然のように行われていますが、Go で開発したり、Haskell で遊んだりしている中で、他のパラダイムの言語で実装するのはどうなんだろうかという想いがありました。 そんな時に出会ったのが、Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F# という本です。 概要 構成 ドメインを理解し、モデリングする 端的なフレーズ Database-Drive-Design や Class-Driven-Design との違…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/1680502549/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/511O5zAOJiL.jpg&quot; alt=&quot;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&quot; title=&quot;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;オブジェクト指向言語でドメインモデルを実装することが当然のように行われていますが、Go で開発したり、Haskell  で遊んだりしている中で、他のパラダイムの言語で実装するのはどうなんだろうかという想いがありました。&lt;/p&gt;

&lt;p&gt;そんな時に出会ったのが、&lt;a href=&quot;http://d.hatena.ne.jp/asin/1680502549/shinxblog-22&quot;&gt;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&lt;/a&gt; という本です。&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#概要&quot;&gt;概要&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#構成&quot;&gt;構成&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#ドメインを理解しモデリングする&quot;&gt;ドメインを理解し、モデリングする&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#端的なフレーズ&quot;&gt;端的なフレーズ&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Database-Drive-Design-や-Class-Driven-Design-との違い&quot;&gt;Database-Drive-Design や Class-Driven-Design との違い&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#型型型&quot;&gt;型、型、型&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#関数型言語による実用例&quot;&gt;関数型言語による実用例&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#恐怖のモナド&quot;&gt;恐怖のモナド&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;概要&quot;&gt;概要&lt;/h2&gt;

&lt;p&gt;本書は、とある会社の受注とその関連業務をドメインとし、モデリングして、実装していくという内容です。紙ベースで行われている業務を Web システムにしていきます。&lt;/p&gt;

&lt;p&gt;オブジェクト指向言語を対象とした書籍は、エリック・エヴァンスの DDD 本をはじめ、いくつかありますが、この本では、関数型言語である F# で実装するのが特徴です。関数型言語で実装するということは、モデリングにも影響を及ぼすことになります。関数型言語で、どのようにドメインをモデリングし、現実的なコードに落としていくのかというというのが読む前の関心でした。&lt;/p&gt;

&lt;p&gt;なお、サンプルコードなどは、F# で実装はされていますが、固有の記述は少ないですし、F# 特有の機能はちゃんと解説があるので、特に問題無く読めました。&lt;/p&gt;

&lt;h2 id=&quot;構成&quot;&gt;構成&lt;/h2&gt;

&lt;p&gt;書籍は、「Understanding the Domain」「Modeling the Domain」「Implementing the Model」の 3 部で構成されています。全体を通じて、同じ業務プロセス（主に受注業務）について、ドメインの理解、モデリング、実装と進んでいくので、DDD を実践する流れが掴みやすいです。個人的には、受注業務というイメージしやすいドメインが対象というのも良かったです。&lt;/p&gt;

&lt;p&gt;3 部の最後にある「Evolving a Design and Keeping It Clean」は、これまでのデザインや実装をベースに仕様変更に対して、どのように対応していくかという内容なので、全てのパートに関連するところなので、第 4 部かなという気がしますが、ボリューム的に 3 部におさめたように感じました。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;1. Understanding the Domain
  * Introducing Domain-Driven Design
  * Understanding the Domain 
  * A Functional Architecture
2. Modeling the Domain
  * Understanding Types 
  * Domain Modeling with Types 
  * Integrity and Consistency in the Domain
  * Modeling Workflows as Pipelines 
3. Implementing the Model
  * Understanding Functions 
  * Implementation: Composing a Pipeline 
  * Implementation: Working with Errors
  * Serialization 
  * Persistence
  * Evolving a Design and Keeping It Clean&lt;/pre&gt;


&lt;h2 id=&quot;ドメインを理解しモデリングする&quot;&gt;ドメインを理解し、モデリングする&lt;/h2&gt;

&lt;p&gt;読む前は、関数型言語による DDD 実装を示すといった内容をイメージしていたのですが、タイトルのとおり、いかにドメインを理解して、モデリングしていくという内容が半分以上占めています。&lt;/p&gt;

&lt;p&gt;はじめに、Event Storming やインタビューから要求を聞き出して、対象ドメインの発見、そして理解を深めていきます。問題領域にあるドメインをサブドメインに分割し、解決領域に境界づけられたコンテキストとして定義します。さらにコンテキスト間の関係をコンテキストマップで示します。&lt;/p&gt;

&lt;p&gt;これで全体像を把握した後に、対象業務のワークフローを題材に必要なドメインを F# の型としてモデリングを進めていきます。各要素の型を組み合わせて、ワークフローのサブステップを関数とし、さらに関数を合成してワークフローを組み上げていきます。&lt;/p&gt;

&lt;p&gt;これらの流れが一気通貫で解説されており、対象ドメインがイメージしやすく、また小さな規模の話ということもあって、関数型言語云々は関係無く、とても分かりやすかったです。&lt;/p&gt;

&lt;h2 id=&quot;端的なフレーズ&quot;&gt;端的なフレーズ&lt;/h2&gt;

&lt;p&gt;DDD や関数型言語に関する知見を短い文で端的に示している箇所がいくつかあり、内容を理解したり、考えを整理する上でとても役立ちました。記憶にも残りやすくて良いですね。&lt;/p&gt;

&lt;p&gt;例えば、下記のような感じです。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;共有モデルを作るガイドライン。

&lt;ul&gt;
&lt;li&gt;データ構造ではなく、ビジネスイベントやワークフローに注目する。&lt;/li&gt;
&lt;li&gt;問題領域では、ドメインを小さなサブドメインに分割する。&lt;/li&gt;
&lt;li&gt;解決領域では、サブドメイン毎にモデルを作る。&lt;/li&gt;
&lt;li&gt;プロジェクトに関わる全ての人で共有され、コードでも利用される共通の言語（ユビキタス言語）を作る。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;問題を理解することは、安易に解決策を作ることではない。&lt;/li&gt;
&lt;li&gt;まず、ビジネスイベントに注目する。&lt;/li&gt;
&lt;li&gt;persistence ignorance&lt;a href=&quot;#f-7f96d0d4&quot; name=&quot;fn-7f96d0d4&quot; title=&quot;blessed ignorance から来てる表現？&quot;&gt;*1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;make illegal states unrepresentable&lt;/li&gt;
&lt;/ul&gt;


&lt;h2 id=&quot;Database-Drive-Design-や-Class-Driven-Design-との違い&quot;&gt;Database-Drive-Design や Class-Driven-Design との違い&lt;/h2&gt;

&lt;p&gt;Domain-Driven 以外の設計方法として、Database-Driven や Class-Driven な方法との違いについても書かれています。データベーステーブルから設計しないということは良く言われているとは思いますが、Class-Drivenについては、やや例示されている内容が恣意的な面もあり、まあそういう見方もあるかなという程度でした。&lt;/p&gt;

&lt;p&gt;いずれにしても、ドメインを理解するフェーズにおいては、システムに関する技術要素を入れないようにするという点は共通のものですね。&lt;/p&gt;

&lt;h2 id=&quot;型型型&quot;&gt;型、型、型&lt;/h2&gt;

&lt;p&gt;対象ドメインについて、とにかく型で表現していきます。代数的データ型の表現力のおかげで、簡潔な記述でドメインが型として表現でき、コンパイラでチェックができるのは良いですね。この簡単に型が書けるというのは、数多くの型を作る上で一つのポイントだと思います。&lt;a href=&quot;#f-0f277f0f&quot; name=&quot;fn-0f277f0f&quot; title=&quot;PHP でも、代数的データ型とパターンマッチが欲しい。。。&quot;&gt;*2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;対象ドメインの受注業務では、注文の状態が「未処理」「未検証」「検証済」「価格算出済 or 無効」といった具合に遷移していきます。状態の管理ということで、ステートマシンの話になるのですが、ここでは、それぞれの状態を別の型にして、これを  Choise Type にしています。&lt;/p&gt;

&lt;pre class=&quot;code F#&quot; data-lang=&quot;F#&quot; data-unlink&gt;type Order = 
  | Unvalidated of UnvalidatedOrder
  | Validated of ValidatedOrder
  | Priced of PricedOrder
  // etc&lt;/pre&gt;


&lt;p&gt;型を分けておけば、状態によって必要なフィールドが異なっていても、それぞれの型で定義すれば良く、無駄なフィールドを持つ必要が無くなります。このような手法を &lt;code&gt;make illegal states unrepresentable&lt;/code&gt;&lt;a href=&quot;#f-890d9e94&quot; name=&quot;fn-890d9e94&quot; title=&quot;https://blog.janestreet.com/effective-ml-revisited/&quot;&gt;*3&lt;/a&gt; と呼ぶようです。&lt;/p&gt;

&lt;p&gt;この値を利用する関数では、パターンマッチによって状態毎の処理を記述します。OOP なら、State パターンで表現できそうですが、この場合、State クラス（とその派生クラス）側で実装を持つ形になります。このあたりは、FP と OOP で表現が異なるので面白いところですね。&lt;/p&gt;

&lt;p&gt;こうした箇所はいくつもあり、例えばメールアドレスが未検証か検証済かなども別の型で定義して、Choice Type でまとめられています。&lt;/p&gt;

&lt;p&gt;とにかく型で表現していくので、「型を大量に作るけど、本当に必要？」という読者の懸念にも触れられていて、必要に応じて減らして下さいとも書かれています。このあたりは現場での導入も考慮されているなと感じました。&lt;/p&gt;

&lt;h2 id=&quot;関数型言語による実用例&quot;&gt;関数型言語による実用例&lt;/h2&gt;

&lt;p&gt;ワークフローを実装する箇所では、必要なサブステップを関数にして、それを合成していきます。合成するにあたって、関数の出力と次の関数の入力の型が合わない箇所への対応についても解説されています。アダプタ関数やモナドを使って、とにかく合成していくというのは関数型らしいです。&lt;/p&gt;

&lt;p&gt;また、部分適用による DI も興味深かったです。関数を元に新しい関数を作って、というのは理解はしていたのですが、永続化処理を行う関数をトップレベルで部分適用して、内包した関数をワークフローに渡すというのは、実用例として分かりやすかったです。OOP でいうと、コンストラクタインジェクションで IO 操作を与えておけば、生成されたインスタンスを使う側はそれを意識せずに使えるというのと同じですね。&lt;/p&gt;

&lt;p&gt;なお、本書では、ドメインに関する実装だけではなく、データベースアクセス処理や外部システムとの連携（DTO とドメインモデルの相互変換）についても解説されており、関数型言語をビジネスアプリケーションで利用する上での実例として参考になりました。&lt;/p&gt;

&lt;h2 id=&quot;恐怖のモナド&quot;&gt;恐怖のモナド&lt;/h2&gt;

&lt;p&gt;エラーハンドリングの章では、モナドを活用にしたモナディック関数の合成についても触れられています。&lt;code&gt;the scary-sounding monad&lt;/code&gt; と表現されていて、やっぱり、そういう扱いなんですね ;)&lt;/p&gt;

&lt;p&gt;本文では、モナドに関する解説無しに実際のコードに適用した後に、モナドについての説明が入ります。下記の文は、端的に表現されていてイメージしやすいです。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;A monad is just a programming pattern that allows you to chain “monadic” functions together in series. OK, then what’s a “monadic” function? It’s a function that takes a “normal” value and returns some kind of “enhanced” value. In the error-handling approach developed in this chapter, the “enhanced” value is something wrapped in the Result type, so a monadic function is exactly the kind of Result-generating “switch” functions that we’ve been working with.&lt;/pre&gt;


&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;DDD と関数型言語による身近な題材による実装テクニックが平易な解説で学べるという、一粒で二度美味しい良い本でした。ドメインの定義やモデリングについては、これまで読んだ本の中で一番イメージしやすかったです。&lt;/p&gt;

&lt;p&gt;この本で、日頃自分が開発している領域についても関数型言語を利用するイメージができました。OOP による DDD の情報が多いですが、もしかすると関数型言語の方が平易に DDD を実装できるのはないかという印象すら持っています。&lt;/p&gt;

&lt;p&gt;関数型言語に興味が無い人も、DDD による設計と型による表現は学ぶべきものがあると思うので、おすすめです。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/1680502549/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/511O5zAOJiL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&quot; title=&quot;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/1680502549/shinxblog-22/&quot;&gt;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; Scott Wlaschin&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; Pragmatic Bookshelf&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2018/02/04&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; ペーパーバック&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/1680502549/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログを見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;下記なら、eBook が DRM フリーです。
&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fpragprog.com%2Fbook%2Fswdddf%2Fdomain-modeling-made-functional&quot; title=&quot;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F# by Scott Wlaschin | The Pragmatic Bookshelf&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://pragprog.com/book/swdddf/domain-modeling-made-functional&quot;&gt;pragprog.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/yasuabe2613/items/5ab33e103e4105630e4c&quot;&gt;&amp;#x95A2;&amp;#x6570;&amp;#x578B;DDD&amp;#x301C;Domain Modeling Made Functional &amp;#x307E;&amp;#x3068;&amp;#x3081; - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-7f96d0d4&quot; name=&quot;f-7f96d0d4&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;blessed ignorance から来てる表現？&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-0f277f0f&quot; name=&quot;f-0f277f0f&quot; class=&quot;footnote-number&quot;&gt;*2&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;PHP でも、代数的データ型とパターンマッチが欲しい。。。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-890d9e94&quot; name=&quot;f-890d9e94&quot; class=&quot;footnote-number&quot;&gt;*3&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;&lt;a href=&quot;https://blog.janestreet.com/effective-ml-revisited/&quot;&gt;https://blog.janestreet.com/effective-ml-revisited/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/511O5zAOJiL.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>Laravel におけるリポジトリ実装のポイント</title>
        <link href="https://blog.shin1x1.com/entry/laravel-repository"/>
        <id>hatenablog://entry/17680117127167158802</id>
        <published>2019-06-21T08:08:00+09:00</published>
        <updated>2019-06-21T08:09:01+09:00</updated>        <summary type="html">Laravel を使った開発でも、ドメインロジックと RDBMS などの永続化層へのアクセスを分離するためにリポジトリパターンを採用するケースが増えてきました。 ただ、Laravel には Eloquent という Active Record タイプの ORM があるので、これとリポジトリをどのように組み合わせるかで悩んでいる人が多いようで、これまで開発現場や勉強会などで質問を受けることがありました。 本エントリでは、リポジトリを実装してきた経験を元に、私が考える実装のポイントをご紹介します。 1. ドメインデータの入出力にリポジトリパターンを使う 2. メソッドの型宣言にドメインデータを指定…</summary>
        <content type="html">&lt;p&gt;Laravel を使った開発でも、ドメインロジックと RDBMS などの永続化層へのアクセスを分離するためにリポジトリパターンを採用するケースが増えてきました。&lt;/p&gt;

&lt;p&gt;ただ、Laravel には Eloquent という Active Record タイプの ORM があるので、これとリポジトリをどのように組み合わせるかで悩んでいる人が多いようで、これまで開発現場や勉強会などで質問を受けることがありました。&lt;/p&gt;

&lt;p&gt;本エントリでは、リポジトリを実装してきた経験を元に、私が考える実装のポイントをご紹介します。&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#1-ドメインデータの入出力にリポジトリパターンを使う&quot;&gt;1. ドメインデータの入出力にリポジトリパターンを使う&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#2-メソッドの型宣言にドメインデータを指定する&quot;&gt;2. メソッドの型宣言にドメインデータを指定する&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#3-機械的に-CRUD-メソッドを実装しない&quot;&gt;3. 機械的に CRUD メソッドを実装しない&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#4-Eloquent-を利用したリポジトリクラスの実装&quot;&gt;4. Eloquent を利用したリポジトリクラスの実装&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#5-複数テーブルを扱うリポジトリ&quot;&gt;5. 複数テーブルを扱うリポジトリ&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#6-Paginator-との連携&quot;&gt;6. Paginator との連携&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1-ドメインデータの入出力にリポジトリパターンを使う&quot;&gt;1. ドメインデータの入出力にリポジトリパターンを使う&lt;/h2&gt;

&lt;p&gt;リポジトリパターンは、ビジネスロジックの実行に必要なデータを RDBMS のようなデータストアから入出力する際に利用します&lt;a href=&quot;#f-d1a0812b&quot; name=&quot;fn-d1a0812b&quot; title=&quot;理想は、入出力すら意識せずに操作できることですが、実際のところ入出力であることは意識して実装することが多いです。&quot;&gt;*1&lt;/a&gt;。あくまで起点となるのはドメインデータです。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;本エントリでは、ビジネスロジックの実行に必要なデータを、DDD で登場する Entity や Value Object、Aggregate などのドメインモデルはもちろんのこと、stdClass や array、 string、int などの組み込みの型も含める意図で、ドメインデータと呼びます。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Eloquent のような Active Record では、1 テーブルにつき、1 クラスといった具合にテーブルを写すものとして実装しますが、リポジトリはそうではありません。一つのテーブルから複数のリポジトリが実装される場合もありますし、複数のテーブルを集約したリポジトリを実装することもあります。&lt;/p&gt;

&lt;p&gt;ドメインデータの入出力のためにリポジトリを使うという点をまず意識します。&lt;/p&gt;

&lt;h2 id=&quot;2-メソッドの型宣言にドメインデータを指定する&quot;&gt;2. メソッドの型宣言にドメインデータを指定する&lt;/h2&gt;

&lt;p&gt;リポジトリのインターフェイスは、1. を意識して定義します。このインターフェイスでは、ドメインデータの入出力を示すので、メソッドの引数や戻り値では、ドメインデータを型として指定します。つまり、Eloquent を引数にしたり戻り値にしたりはしないということです。&lt;/p&gt;

&lt;p&gt;下記では、UserRepository という User モデルの入出力を担うリポジトリインターフェイスを定義しています。この User は、POPO(Plain Old PHP Object) で実装したドメインデータであり、Eloquent ではないので注意して下さい。&lt;/p&gt;

&lt;p&gt;findById メソッドでは、UserId クラス $id を引数に取り&lt;a href=&quot;#f-90af5a7c&quot; name=&quot;fn-90af5a7c&quot; title=&quot;int のような組み込みの型でも構いません。&quot;&gt;*2&lt;/a&gt;、User モデルを返しています。store メソッドでは、User モデルを引数に取ります。&lt;/p&gt;

&lt;p&gt;このインターフェイスを見れば、User モデルに関するリポジトリであることは分かりますが、実際にどのような方法でデータストアへアクセスして入出力を行うのかは分かりません。リポジトリをドメインデータの入出力を行うものとして捉えれば、これで十分なわけです。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Domain\Repository;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\User;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\UserId;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; UserRepository
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId $id&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findById&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;UserId &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; User;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;User $user&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;User &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; UserId;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;h2 id=&quot;3-機械的に-CRUD-メソッドを実装しない&quot;&gt;3. 機械的に CRUD メソッドを実装しない&lt;/h2&gt;

&lt;p&gt;先に書いたとおり、リポジトリはドメインデータの入出力を行うものです。つまり、ドメインデータとして必要な入出力のみを定義すれば良いのです。アプリケーションに、あるドメインデータを削除する概念が無いのなら、remove メソッドは不要です。読み取る際も、ID がキーなのか、Email なのか、複数の条件なのかはドメインデータによって様々です。&lt;/p&gt;

&lt;p&gt;また、結果としては同じ users テーブルに対する保存であっても、上記の UserRepostitory::store メソッドのようにモデルをそのまま永続化するメソッドがあったり、ユースケースによってはメールアドレスだけを保存するメソッドがあっても構わないということです。&lt;/p&gt;

&lt;p&gt;機械的に CRUD を付けるというのは、データベーステーブルに対応するリポジトリを実装するという発想から来るものでしょう。そうではなく、リポジトリでは、対象のドメインデータが永続化層にどのような操作が必要かを考えて、それのみを実装すると良いです。&lt;/p&gt;

&lt;h2 id=&quot;4-Eloquent-を利用したリポジトリクラスの実装&quot;&gt;4. Eloquent を利用したリポジトリクラスの実装&lt;/h2&gt;

&lt;p&gt;リポジトリのインターフェイスさえ決まれば、後の実装は簡単です。インターフェイスの定義を満たすように実装していくだけです。&lt;/p&gt;

&lt;p&gt;下記では、UserRepository インターフェイスを Eloquent を使って実装しています。EloquentUser は、users テーブルに対する Eloquent です。findById メソッド、store メソッド共に、この EloquentUser を使って実装しています。&lt;/p&gt;

&lt;p&gt;findById メソッドでは、EloquentUser を使って、レコードを取得し、その結果を User モデルにセットして返しています。&lt;/p&gt;

&lt;p&gt;store メソッドでは、User モデルを引数に取っており、必要な EloquentUser インスタンスにセットして保存しています。&lt;/p&gt;

&lt;p&gt;このようにドメインデータを入出力に使うようにインターフェイスを定義しておけば、その実装に Eloquent  を利用しても、それはあくまでリポジトリクラス内部の実装方法に過ぎず、何ら問題ありません。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Infrastructure\Repository;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\&lt;span class=&quot;synIdentifier&quot;&gt;Exception&lt;/span&gt;\NotFoundException;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\User;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\UserId;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Repository\UserRepository;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Infrastructure\Eloquent\EloquentUser;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; EloquentUserRepository &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt; UserRepository
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/** &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;EloquentUser */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;EloquentUser $eloquentUser&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;EloquentUser &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId $id&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@throws &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;NotFoundException&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findById&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;UserId &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; User
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synComment&quot;&gt;/** &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;EloquentUser $eloquent */&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;newQuery&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
        &lt;span class=&quot;synComment&quot;&gt;// Facade を使っても構わない&lt;/span&gt;
        &lt;span class=&quot;synComment&quot;&gt;// $eloquent = EloquentUser::find($id-&amp;gt;asInt());&lt;/span&gt;
        
        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; NotFoundException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;user not found:&#39;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; User&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; UserId&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;id&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;email
        &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;User $user&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;User &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; UserId
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;hasId&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;newQuery&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findOrFail&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;getId&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;newInstance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;getName&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;email &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;getEmail&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; UserId&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquent&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;id&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;h2 id=&quot;5-複数テーブルを扱うリポジトリ&quot;&gt;5. 複数テーブルを扱うリポジトリ&lt;/h2&gt;

&lt;p&gt;これまで述べたとおり、リポジトリはテーブルと 1 対 1 で実装する必要はありません。つまり、複数テーブルからデータを取得して、一つのドメインデータとして返す場合もあります。&lt;/p&gt;

&lt;p&gt;下記は、PointEmail モデルを取得する PointEmailRepository です。findByUserId メソッドで PointEmail モデルを取得します。このインターフェイスでは、どのテーブルのどのカラムから値を取得するかは分からず、また知る必要もありません。分かるのは、UserId を渡せば、PointEmail が返ってくるということだけです。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Domain\Repository;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\PointEmail;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\UserId;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; PointEmailRepository
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId $id&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;PointEmail&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findByUserId&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;UserId &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; PointEmail;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;PointEmail をデータベースから取得するには、users.email と user_points.point が必要となります。これを実装しているのが、下記の EloquentPointEmailRepository です。findByUserId メソッド内では、テーブルを join してレコードを取得し、それを PointEmail インスタンスとして返しています。&lt;/p&gt;

&lt;p&gt;このように複数テーブルから値を取得する際もレコードを取得して、ドメインデータにセットして返せば良いのです。&lt;/p&gt;

&lt;p&gt;もし、ドメインデータに必要な値のデータソースが異なる場合、例えば、データベースにある値と Redis にある値が必要な場合でも理屈は同じです。リポジトリインターフェイスでは、ドメインデータを返す定義を書いておき、それぞれのデータソースからの取得処理はリポジトリクラスで書けば良いのです。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Infrastructure\Repository;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\&lt;span class=&quot;synIdentifier&quot;&gt;Exception&lt;/span&gt;\NotFoundException;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\PointEmail;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Model\UserId;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Domain\Repository\PointEmailRepository;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Infrastructure\Eloquent\EloquentUser;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; EloquentPointEmailRepository &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt; PointEmailRepository
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/** &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;EloquentUser */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;EloquentUser $eloquentUser&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;EloquentUser &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;eloquentUser&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;UserId $id&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;PointEmail&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@throws &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;NotFoundException&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findByUserId&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;UserId &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; PointEmail
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;eloquentUser&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;newQuery&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;join&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;user_points&#39;&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;&#39;user_points.user_id&#39;&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;&#39;=&#39;&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;&#39;users.id&#39;&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;where&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;id&#39;&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;first&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;email&#39;&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;&#39;point&#39;&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; NotFoundException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;PointEmail not found:&#39;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; PointEmail&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;email,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;point
        &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;これはデータストアへの保存についても同じです。一つのドメインデータを複数テーブルに保存する場合も良くありますが、その時はリポジトリの中でトランザクションを開始して、各テーブルにデータを保存した後にコミットすると良いでしょう。もちろん、ケースバイケースにはなりますが、ドメインデータを入出力するリポジトリという視点ではこれがやりやすいです。&lt;/p&gt;

&lt;h2 id=&quot;6-Paginator-との連携&quot;&gt;6. Paginator との連携&lt;/h2&gt;

&lt;p&gt;Laravel は、Eloquent との連携で便利に使える機構があります。例えば、Paginator はその一つでしょう。Eloquent を使えば、paginate メソッドで簡単にデータベースから必要な値を取得して、ビューに渡す Paginator インスタンスを生成できます。&lt;/p&gt;

&lt;p&gt;一方、リポジトリを実装した場合、Eloquent はリポジトリの中で閉じており、戻り値にはドメインデータが返されるのでこうした機構が利用できないように見えます。&lt;/p&gt;

&lt;p&gt;実は、Paginator は、データベースへのアクセスと Paginator の処理は独立しており、下記のようなコードで任意のデータに対して Paginator インスタンスを生成できます。つまり、総件数の算出やデータの抽出はリポジトリで行って、後は Paginator（下記では、LengthAwarePaginator ）インスタンスに与えるだけです。&lt;/p&gt;

&lt;p&gt;これをビューファイルに渡せば、ページングなどに必要なリンクを blade で出力できます。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Application\Action;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Application\Repository\UserPaginatorRepository;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Illuminate\Pagination\LengthAwarePaginator;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Illuminate\Pagination\Paginator;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; UserListAction
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;const&lt;/span&gt; PER_PAGE &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;10&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__invoke&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;UserPaginatorRepository &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; Paginator&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;resolveCurrentPage&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        
        &lt;span class=&quot;synComment&quot;&gt;// リポジトリで総件数と 1 ページ分のデータを取得&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findUserList&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;PER_PAGE&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;synComment&quot;&gt;// LengthAwarePaginator インスタンスの生成&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;paginator&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; LengthAwarePaginator&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;results&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;total&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;PER_PAGE, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; view&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;&#39;users.list&#39;&lt;/span&gt;, &lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;synConstant&quot;&gt;&#39;paginator&#39;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;paginator&lt;/span&gt;,
        &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;もう一つの考え方としては、本エントリの趣旨からはずれますが、Paginator はビュー（プレゼンテーション）の関心事なので、Eloquent を生で使って実装するという方法もあります。このあたりは、何を目的にリポジトリを使うのかという観点で検討すれば良いでしょう。&lt;/p&gt;

&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;リポジトリの実装では、どのようにリポジトリインターフェイスを定義するかが肝になります。これが上手くいけば、後はそれを満たす実装を書くだけなので、迷うことは少なくなります。&lt;/p&gt;

&lt;p&gt;本エントリのように、ドメインデータを引数や戻り値に指定するインターフェイスを書けば、 リポジトリクラスでは、インターフェイスに沿って、データストアからデータを取得して、目的のデータ構造で返すだけなので、極論を言えば、Eloquent で実装しようが、Query Builder で実装しようが、Doctrine で実装しようが、それは実装の詳細に過ぎないということが分かるでしょう。&lt;a href=&quot;#f-2e3ff718&quot; name=&quot;fn-2e3ff718&quot; title=&quot;もちろん、実装の詳細は重要なので、データストアへのアクセスをどう実装するのが良いかというは視点では十分に検討する必要があります&quot;&gt;*3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laravel には、標準で Eloquent と Query Builder という強力な DAO が備わっているので、それらを活用したい人は多いでしょう。これまで示してきたように、Eloquent を利用してリポジトリを実装することは何ら問題無いので、上手く活用していきましょう。&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-d1a0812b&quot; name=&quot;f-d1a0812b&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;理想は、入出力すら意識せずに操作できることですが、実際のところ入出力であることは意識して実装することが多いです。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-90af5a7c&quot; name=&quot;f-90af5a7c&quot; class=&quot;footnote-number&quot;&gt;*2&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;int のような組み込みの型でも構いません。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-2e3ff718&quot; name=&quot;f-2e3ff718&quot; class=&quot;footnote-number&quot;&gt;*3&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;もちろん、実装の詳細は重要なので、データストアへのアクセスをどう実装するのが良いかというは視点では十分に検討する必要があります&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <category term="php" label="php" />
        
        <category term="Laravel" label="Laravel" />
        
        <category term="architecture" label="architecture" />
        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20190621/20190621080715.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>rerun - コマンドが終了したら自動で再実行するツールを作りました</title>
        <link href="https://blog.shin1x1.com/entry/rerun"/>
        <id>hatenablog://entry/17680117127170906580</id>
        <published>2019-05-29T00:00:00+09:00</published>
        <updated>2019-05-29T00:00:29+09:00</updated>        <summary type="html">rerun というコマンドランナーを Go で書きました。rerun を使って、コマンドを実行すると、コマンドが終了した際に再実行してくれます。systemd や supervisord の簡易版と言えばイメージしやすいかもしれません。 github.com 動作イメージ 動機 rerun インストール さいごに 動作イメージ 下記では、rerun で sample.sh が終了する度に再実行しています。 動機 Kubernetes クラスタを Mac 上の Docker Desktop 環境で開発している際、実行状況が分かるように、いくつかのログをターミナルで出力していました。例えば、kub…</summary>
        <content type="html">&lt;p&gt;&lt;code&gt;rerun&lt;/code&gt; というコマンドランナーを Go で書きました。rerun を使って、コマンドを実行すると、コマンドが終了した際に再実行してくれます。systemd や supervisord の簡易版と言えばイメージしやすいかもしれません。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Frerun&quot; title=&quot;shin1x1/rerun&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/rerun&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#動作イメージ&quot;&gt;動作イメージ&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#動機&quot;&gt;動機&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#rerun&quot;&gt;rerun&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#インストール&quot;&gt;インストール&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;動作イメージ&quot;&gt;動作イメージ&lt;/h2&gt;

&lt;p&gt;下記では、rerun で sample.sh が終了する度に再実行しています。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/88324/58423474-2db68500-80d0-11e9-9816-8f36b026e1ac.gif&quot; alt=&quot;screencast&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;動機&quot;&gt;動機&lt;/h2&gt;

&lt;p&gt;Kubernetes クラスタを Mac 上の Docker Desktop 環境で開発している際、実行状況が分かるように、いくつかのログをターミナルで出力していました。例えば、&lt;code&gt;kubectl get events -w&lt;/code&gt; などです。ただ、開発中に Docker Desktop が落ちることがあると、&lt;code&gt;kubectl get events&lt;/code&gt; も一緒に終了してしまうので、Docker Desktop に合わせて、それらもいちいち起動し直すのが面倒でした。&lt;/p&gt;

&lt;p&gt;そこで、こうした手間を省くために作ったのが、rerun です。&lt;/p&gt;

&lt;h2 id=&quot;rerun&quot;&gt;rerun&lt;/h2&gt;

&lt;p&gt;rerun コマンドの後に実行したいコマンドを指定します。rerun を実行すると、指定したコマンドを実行します。もし、コマンドが終了した場合、一定秒数経過後（デフォルト: 10s）に再びコマンドを実行します。&lt;/p&gt;

&lt;pre class=&quot;code lang-sh&quot; data-lang=&quot;sh&quot; data-unlink&gt;&lt;span class=&quot;synComment&quot;&gt;# sample.sh が終了したら、10 秒後に再実行&lt;/span&gt;
$ rerun ./sample.sh
&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;-s&lt;/code&gt; オプションで、再実行までの秒数を指定できます。&lt;/p&gt;

&lt;pre class=&quot;code lang-sh&quot; data-lang=&quot;sh&quot; data-unlink&gt;&lt;span class=&quot;synComment&quot;&gt;# sample.sh が終了したら、1 秒後に再実行&lt;/span&gt;
$ rerun &lt;span class=&quot;synSpecial&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt; ./sample.sh
&lt;/pre&gt;


&lt;p&gt;実行したいコマンドにハイフン &lt;code&gt;-&lt;/code&gt; が含まれる場合は、コマンドの前に &lt;code&gt;—&lt;/code&gt; を付けて実行します。&lt;/p&gt;

&lt;pre class=&quot;code lang-sh&quot; data-lang=&quot;sh&quot; data-unlink&gt;$ rerun &lt;span class=&quot;synSpecial&quot;&gt;--&lt;/span&gt; kubectl get events &lt;span class=&quot;synSpecial&quot;&gt;-w&lt;/span&gt;
&lt;/pre&gt;


&lt;h2 id=&quot;インストール&quot;&gt;インストール&lt;/h2&gt;

&lt;p&gt;rerun は、Go で実装したシングルバイナリなので、macOS / Linux 環境では、下記サイトよりバイナリをダウンロードすれば利用できます。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/shin1x1/rerun/releases&quot;&gt;https://github.com/shin1x1/rerun/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go get&lt;/code&gt; でもインストールできます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ go get -u github.com/shin1x1/rerun&lt;/pre&gt;


&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;簡単な仕組みのツールですが、個人的には重宝していて、わりと便利に使ってます。最近は、下記のように Docker Desktop 自体を rerun で起動して、Docker が落ちても自動で起動するようにしています。&lt;/p&gt;

&lt;pre class=&quot;code bash&quot; data-lang=&quot;bash&quot; data-unlink&gt;$ rerun /Applications/Docker.app/Contents/MacOS/Docker&lt;/pre&gt;


&lt;p&gt;サーバのデーモンを起動したりするなら、systemd なり、supervisord などを使う方が良いと思いますが、開発用端末ではわりと使えているので、同じようなユースケースがあれば試してみてください。&lt;/p&gt;
</content>        
        <category term="golang" label="golang" />
        
        <category term="tool" label="tool" />
        
        <link rel="enclosure" href="https://user-images.githubusercontent.com/88324/58423474-2db68500-80d0-11e9-9816-8f36b026e1ac.gif" type="image/gif" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>yae - YAML alias を展開して出力するツールを作りました</title>
        <link href="https://blog.shin1x1.com/entry/yae"/>
        <id>hatenablog://entry/17680117127143008114</id>
        <published>2019-05-20T14:39:31+09:00</published>
        <updated>2019-05-26T09:29:43+09:00</updated>        <summary type="html">yae という YAML の alias を anchor の内容に展開した YAML を出力するコマンドを Go で書きました。 github.com YAML anchor/alias yae インストール 利用方法 元の YAML との比較 YAML anchor/alias YAML では、anchor と alias を利用することで、共通のノードをまとめることができます。 下記が、anchor を alias を利用した例です。&amp;book が anchor となっており、*book が alias となっています。これは、&amp;book の内容が、 *book の箇所に展開されるイメー…</summary>
        <content type="html">&lt;p&gt;&lt;code&gt;yae&lt;/code&gt; という YAML の alias を anchor の内容に展開した YAML を出力するコマンドを Go で書きました。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Fyae&quot; title=&quot;shin1x1/yae&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/yae&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#YAML-anchoralias&quot;&gt;YAML anchor/alias&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#yae&quot;&gt;yae&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#インストール&quot;&gt;インストール&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#利用方法&quot;&gt;利用方法&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#元の-YAML-との比較&quot;&gt;元の YAML との比較&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;YAML-anchoralias&quot;&gt;YAML anchor/alias&lt;/h2&gt;

&lt;p&gt;YAML では、anchor と alias を利用することで、共通のノードをまとめることができます。&lt;/p&gt;

&lt;p&gt;下記が、anchor を alias を利用した例です。&lt;code&gt;&amp;amp;book&lt;/code&gt; が anchor となっており、&lt;code&gt;*book&lt;/code&gt; が alias となっています。これは、&lt;code&gt;&amp;amp;book&lt;/code&gt; の内容が、 &lt;code&gt;*book&lt;/code&gt; の箇所に展開されるイメージです。また、&lt;code&gt;&amp;lt;&amp;lt;:&lt;/code&gt; を使うことで、anchor の内容の一部のキーを上書きすることもできます。&lt;/p&gt;

&lt;pre class=&quot;code lang-yaml&quot; data-lang=&quot;yaml&quot; data-unlink&gt;&lt;span class=&quot;synIdentifier&quot;&gt;anchors&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;synIdentifier&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;&amp;amp;book&lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt; # anchor&lt;/span&gt;
      &lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; book
      &lt;span class=&quot;synIdentifier&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;synIdentifier&quot;&gt;stocks&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;*book&lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt; # alias&lt;/span&gt;
  &lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;&amp;lt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;*book&lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt; # merge key&lt;/span&gt;
    &lt;span class=&quot;synIdentifier&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt; # &amp;amp;book の price を上書き&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;上の YAML を anchor / alias を使わずに書くと下記のようになります。&lt;/p&gt;

&lt;pre class=&quot;code lang-yaml&quot; data-lang=&quot;yaml&quot; data-unlink&gt;&lt;span class=&quot;synIdentifier&quot;&gt;anchors&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;synIdentifier&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; book
    &lt;span class=&quot;synIdentifier&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;synIdentifier&quot;&gt;stocks&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; book
  &lt;span class=&quot;synIdentifier&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;- &lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; book
  &lt;span class=&quot;synIdentifier&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;200&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;&lt;a href=&quot;https://yaml.org/spec/1.2/spec.html&quot;&gt;https://yaml.org/spec/1.2/spec.html&lt;/a&gt;
&lt;a href=&quot;https://yaml.org/type/merge.html&quot;&gt;https://yaml.org/type/merge.html&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;yae&quot;&gt;yae&lt;/h2&gt;

&lt;p&gt;YAML の alias が実際にどのように展開されるかを確認するためにこのツールを作りました。&lt;/p&gt;

&lt;p&gt;また、長い YAML を便利な anchor / alias を使って、リファクタリングする時に、元の YAML とリファクタリング後の YAML が同じ内容になっているかを比較するのにも利用できます。&lt;/p&gt;

&lt;p&gt;Go で実装したのですが、単に YAML を読んで、出力しているだけです。Go は、こういう小さなツールをサクッと作るのに、ほんと便利ですね。&lt;/p&gt;

&lt;h3 id=&quot;インストール&quot;&gt;インストール&lt;/h3&gt;

&lt;p&gt;yae は、Go で実装したシングルバイナリなので、macOS / Linux 環境では、下記サイトよりバイナリをダウンロードすれば利用できます。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/shin1x1/yae/releases&quot;&gt;https://github.com/shin1x1/yae/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go get&lt;/code&gt; でもインストールできます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ go get -u github.com/shin1x1/yae&lt;/pre&gt;


&lt;h3 id=&quot;利用方法&quot;&gt;利用方法&lt;/h3&gt;

&lt;p&gt;yae コマンドで alias を展開したい YAML ファイルを指定するだけです。実行すると、標準出力に展開された YAML  が出力されます。なお、出力される YAML は、意味として元のもとと同じですが、インデントなどは整形されます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ cat src.yaml
---
anchors:
  items:
    - &amp;amp;book
      name: book
      price: 100

stocks:
  - *book
  - &amp;lt;&amp;lt;: *book
    price: 200

$ yae src.yaml
anchors:
  items:
  - name: book
    price: 100
stocks:
- name: book
  price: 100
- name: book
  price: 200&lt;/pre&gt;


&lt;h3 id=&quot;元の-YAML-との比較&quot;&gt;元の YAML との比較&lt;/h3&gt;

&lt;p&gt;上述したように、このコマンドでは、リストのインデントやキーの並びのように YAML として意味が変わらない部分は変更される場合があるので、diff を取る場合は、下記のように元の YAML も yae コマンドを通して比較した方が余計な差分が出なくて良いです。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ diff &amp;lt;(yae base.yaml) &amp;lt;(yae refactored.yaml)&lt;/pre&gt;

</content>        
        <category term="golang" label="golang" />
        
        <category term="tool" label="tool" />
        
        <link rel="enclosure" href="https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「フレームワークとの付き合い方」を発表しました / Laravel JP Conference</title>
        <link href="https://blog.shin1x1.com/entry/laravel-jp-con"/>
        <id>hatenablog://entry/17680117126965715409</id>
        <published>2019-02-16T15:30:33+09:00</published>
        <updated>2019-02-17T00:16:40+09:00</updated>        <summary type="html">Laravel JP Conference にて「フレームワークとの付き合い方」を発表しました。 発表資料 Toggeter: https://togetter.com/li/1319786 フレームワークの付き合い方について、自分なりの考えをお話しました。アップしたスライドには、発表用スライドではカット部分などを含めています。 途中で、マイクが使えなくなったりましたが、地声で届く範囲だったのでそのまま続行しました :) マイクを持たないと両手がフリーになるので、身振り手振りがやりやすくて話やすくなるというのは大きな発見でした。 セッションを聞いていいただいた方、ありがとうございました。 グラ…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;https://conference2019.laravel.jp/&quot;&gt;Laravel JP Conference&lt;/a&gt; にて「フレームワークとの付き合い方」を発表しました。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;ae84e96981e9434b96fa0a9d541ecff3&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;ul&gt;
&lt;li&gt;Toggeter: &lt;a href=&quot;https://togetter.com/li/1319786&quot;&gt;https://togetter.com/li/1319786&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;フレームワークの付き合い方について、自分なりの考えをお話しました。アップしたスライドには、発表用スライドではカット部分などを含めています。&lt;/p&gt;

&lt;p&gt;途中で、マイクが使えなくなったりましたが、地声で届く範囲だったのでそのまま続行しました :) マイクを持たないと両手がフリーになるので、身振り手振りがやりやすくて話やすくなるというのは大きな発見でした。&lt;/p&gt;

&lt;p&gt;セッションを聞いていいただいた方、ありがとうございました。&lt;/p&gt;

&lt;h2&gt;グラレコ&lt;/h2&gt;

&lt;p&gt;@kozzy0919 さんがグラレコでまとめて下さいました。ありがとうございます！&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/hashtag/laraveljpcon?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#laraveljpcon&lt;/a&gt; で「フレームワークとの付き合い方」を講演いただいた &lt;a href=&quot;https://twitter.com/shin1x1?ref_src=twsrc%5Etfw&quot;&gt;@shin1x1&lt;/a&gt;&lt;br&gt;さんに許可をいただいたので、内容をデジタルグラレコした画像をシェアします〜。&lt;a href=&quot;https://twitter.com/shin1x1?ref_src=twsrc%5Etfw&quot;&gt;@shin1x1&lt;/a&gt; さん、ありがとうございました！ &lt;a href=&quot;https://t.co/azLj3D454p&quot;&gt;pic.twitter.com/azLj3D454p&lt;/a&gt;&lt;/p&gt;&amp;mdash; kozzy (@kozzy0919) &lt;a href=&quot;https://twitter.com/kozzy0919/status/1096622110955397120?ref_src=twsrc%5Etfw&quot;&gt;February 16, 2019&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;h2&gt;Ask the speaker&lt;/h2&gt;

&lt;p&gt;セッションの後は、Ask the speaker で質疑応答を行いました。何名かの方に質問に来て頂いて、内容についてお話したのですが、じっくりとフィードバックや質問を聞いて、PC をお互いに見ながら落ち着いて話ができたのでとても良かったです。&lt;/p&gt;

&lt;p&gt;質疑応答は、会場でみんなで共有するのが良いと思っていましたが、今終わったセッションをネタに議論ができるので面白かったです。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;Laravel というフレームワークをテーマにしたイベントということもあったのか、いつもの PHP 系イベントに参加しない方も多かったようで新鮮でした。&lt;/p&gt;

&lt;p&gt;スタッフのみなさん、楽しいイベントをありがとうございました！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20190216/20190216153143.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「明日から使えるアーキテクチャ 独立したコアレイヤパターン」を発表しました / PHP カンファレンス仙台 2019</title>
        <link href="https://blog.shin1x1.com/entry/independent-core-layer-pattern-phpconsen2019"/>
        <id>hatenablog://entry/98012380844378806</id>
        <published>2019-01-29T00:00:00+09:00</published>
        <updated>2019-01-29T00:00:53+09:00</updated>        <summary type="html">仙台初開催の PHP カンファレンス仙台 2019 にて「明日から使えるアーキテクチャ 独立したコアレイヤパターン」を発表しました。 発表資料 サンプルコード: https://github.com/shin1x1/phpconsen2019 Toggeter: https://togetter.com/li/1313010 独立したコアレイヤパターンについて発表しました。当初は、昨年末に東京で行われた PHP カンファレンス 2018 で発表したものと同じ内容にしようと考えていたのですが、下記のような考えももあり、資料を大幅に変更しています。 色々なフィードバックを受けて、話して、現場で適用…</summary>
        <content type="html">&lt;p&gt;仙台初開催の &lt;a href=&quot;https://phpcon-sendai.net/2019/&quot;&gt;PHP カンファレンス仙台 2019&lt;/a&gt; にて「明日から使えるアーキテクチャ 独立したコアレイヤパターン」を発表しました。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;45220513724a443fa2aa24e6af771f2c&quot; data-ratio=&quot;1.33159947984395&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;ul&gt;
&lt;li&gt;サンプルコード: &lt;a href=&quot;https://github.com/shin1x1/phpconsen2019&quot;&gt;https://github.com/shin1x1/phpconsen2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Toggeter: &lt;a href=&quot;https://togetter.com/li/1313010&quot;&gt;https://togetter.com/li/1313010&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;独立したコアレイヤパターンについて発表しました。当初は、昨年末に東京で行われた PHP カンファレンス 2018 で発表したものと同じ内容にしようと考えていたのですが、下記のような考えももあり、資料を大幅に変更しています。&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;色々なフィードバックを受けて、話して、現場で適用してと繰り返す内に、このパターンにおいて本質でないものが濾過されていって、コアの部分（レイヤの名前じゃなく）が残ってきた。そのあたりを今日話したい。&lt;/p&gt;&amp;mdash; Masashi Shinbara (@shin1x1) &lt;a href=&quot;https://twitter.com/shin1x1/status/1088938344623497216?ref_src=twsrc%5Etfw&quot;&gt;January 25, 2019&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;p&gt;今回は、既に実装されている API の実装にコアレイヤパターンを適用するというデモを入れました。やっぱりデモを入れた方が、具体的にイメージしやすいですし、説明もしやすいですね。やって良かったです。&lt;/p&gt;

&lt;p&gt;PHP カンファレンスで発表したものと合わせて見ていただければ理解が進むと思うので、こちらもどうぞ。&lt;/p&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;b7f429f2574f48c2bccec1898a9315ec&quot; data-ratio=&quot;1.33159947984395&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;ul&gt;
&lt;li&gt;サンプルコード: &lt;a href=&quot;https://github.com/shin1x1/phpcon2018-independent-core-layer&quot;&gt;https://github.com/shin1x1/phpcon2018-independent-core-layer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Togetter: &lt;a href=&quot;https://togetter.com/li/1299453&quot;&gt;https://togetter.com/li/1299453&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;スーパーヒーローがいなくても使えるパターン&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-slide=&quot;41&quot; data-id=&quot;45220513724a443fa2aa24e6af771f2c&quot; data-ratio=&quot;1.33159947984395&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;今回の発表で伝えたかったことの一つがこれです。レイヤ分けや抽象を利用した分離は、アーキテクチャを積極的に学んでいる人やオブジェクト指向開発に詳しい人だけが使えるというものではありません。今回発表したようなレイヤ分けの手順を踏めば、適用することが可能です。&lt;/p&gt;

&lt;p&gt;もちろん、実際の現場では考慮すべき点は出てきますが、少なくともレイヤを分けて、インターフェイスを抽出し、アダプタを実装するというベースを作るところまでは機械的にできるでしょう。&lt;/p&gt;

&lt;p&gt;興味を持った方は、試しに、日頃から取り組んでいるコードに対してパターンを適用してみて、その感覚を体験してみて下さい。&lt;/p&gt;

&lt;h2&gt;質疑応答&lt;/h2&gt;

&lt;p&gt;発表後の質疑応答にていくつか質問を頂きました。その中で、実際にこのパターンを適用されている方からの質問がありました！DDD + 独立したコアレイヤパターンという構成で開発されているとのことです。私が関わっていないプロジェクトでも活用されているのを聞くと嬉しいですね。どこかで機会があれば、詳しくお話聞ければ嬉しいです。&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;空気読まず、2つ質問してみた結果、モヤモヤしていた疑問が解決した！&lt;br&gt;今の大きめのプロジェクトは、独立したコアレイヤパターンwith DDDスタイルで書いていて見通しが非常に良くなっている。 &lt;a href=&quot;https://t.co/dowEcJ62BJ&quot;&gt;https://t.co/dowEcJ62BJ&lt;/a&gt;&lt;/p&gt;&amp;mdash; 高木 翔太 (@0su43) &lt;a href=&quot;https://twitter.com/0su43/status/1089028067433930752?ref_src=twsrc%5Etfw&quot;&gt;January 26, 2019&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;カンファレンスでは、興味を惹かれるセッションも多く、ラフに話ができるアンカンファレンスなどもあって、楽しい時間を過ごすことができました。個人的には、カンファレンス会場と同じビルにホテルがあったおかげで、イベントの合間にも部屋に戻って荷物を置いたり、休憩できたりと快適でした :)&lt;/p&gt;

&lt;p&gt;実行委員長の五十嵐さんが話されていた、こうしたイベントを開くことで仙台という地域を盛り上げていきたい、という想いには共感するところがあり、これをきっかけに盛り上がっていくといいなと思います。&lt;/p&gt;

&lt;p&gt;スタッフのみなさん、楽しいイベントをありがとうございました！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20190128/20190128224602.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>Vue.js + Vuex + TypeScript ファーストインプレッション</title>
        <link href="https://blog.shin1x1.com/entry/vuejs-vuex-typescript"/>
        <id>hatenablog://entry/10257846132670357232</id>
        <published>2018-11-15T22:40:00+09:00</published>
        <updated>2018-11-15T22:52:21+09:00</updated>        <summary type="html">日頃は、サーバサイドの開発が主なのですが、その UI として フロントエンドの開発も行うことがあります。最近話題の Vue.js を試してみたのでその感想などを。 前提 これまで利用した構成 Vue.js 構成 雑感 ちょっと試す、がすぐにできる vue-cli が楽 vue ファイル template ディレクティブ scoped css vue-router / Vuex 連携 Vuex mutation を直接呼んで、state を変更 コンポーネントから直接 state を操作 TypeScript Vue.js 入門 さいごに 前提 これまで利用した構成 ベースとして、これまでプロダ…</summary>
        <content type="html">&lt;p&gt;日頃は、サーバサイドの開発が主なのですが、その UI として フロントエンドの開発も行うことがあります。最近話題の Vue.js を試してみたのでその感想などを。&lt;/p&gt;

&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#前提&quot;&gt;前提&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#これまで利用した構成&quot;&gt;これまで利用した構成&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#Vuejs-構成&quot;&gt;Vue.js 構成&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#雑感&quot;&gt;雑感&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#ちょっと試すがすぐにできる&quot;&gt;ちょっと試す、がすぐにできる&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#vue-cli-が楽&quot;&gt;vue-cli が楽&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#vue-ファイル&quot;&gt;vue ファイル&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#template-ディレクティブ&quot;&gt;template ディレクティブ&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#scoped-css&quot;&gt;scoped css&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#vue-router--Vuex-連携&quot;&gt;vue-router / Vuex 連携&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#Vuex&quot;&gt;Vuex&lt;/a&gt;&lt;ul&gt;
                    &lt;li&gt;&lt;a href=&quot;#mutation-を直接呼んでstate-を変更&quot;&gt;mutation を直接呼んで、state を変更&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;#コンポーネントから直接-state-を操作&quot;&gt;コンポーネントから直接 state を操作&lt;/a&gt;&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#TypeScript&quot;&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#Vuejs-入門&quot;&gt;Vue.js 入門&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;前提&quot;&gt;前提&lt;/h2&gt;

&lt;h3 id=&quot;これまで利用した構成&quot;&gt;これまで利用した構成&lt;/h3&gt;

&lt;p&gt;ベースとして、これまでプロダクションで利用したことがある構成は以下です。いずれも、フォームベースの SPA で、現在も稼働しています。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AngularJS 1.x&lt;/li&gt;
&lt;li&gt;AngularJS 1.x + TypeScript&lt;/li&gt;
&lt;li&gt;React + Redux&lt;/li&gt;
&lt;li&gt;React + MobX + TypeScript&lt;/li&gt;
&lt;/ul&gt;


&lt;h3 id=&quot;Vuejs-構成&quot;&gt;Vue.js 構成&lt;/h3&gt;

&lt;p&gt;以下の構成で、ログイン、一覧表示、フォームを API サーバと通信して動作させるところまで実装してみました。&lt;/p&gt;

&lt;pre class=&quot;code lang-json&quot; data-lang=&quot;json&quot; data-unlink&gt;  &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;dependencies&lt;/span&gt;&amp;quot;: &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;axios&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^0.18.0&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;bootstrap&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^4.1.3&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;jquery&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^3.3.1&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;popper.js&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^1.14.5&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vue&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^2.5.17&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vue-class-component&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^6.0.0&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vue-property-decorator&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^7.0.0&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vue-router&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^3.0.1&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vuex&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^3.0.1&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vuex-class&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^0.3.1&lt;/span&gt;&amp;quot;,
    &amp;quot;&lt;span class=&quot;synStatement&quot;&gt;vuex-router-sync&lt;/span&gt;&amp;quot;: &amp;quot;&lt;span class=&quot;synConstant&quot;&gt;^5.0.0&lt;/span&gt;&amp;quot;
  &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;,
&lt;/pre&gt;


&lt;h2 id=&quot;雑感&quot;&gt;雑感&lt;/h2&gt;

&lt;h3 id=&quot;ちょっと試すがすぐにできる&quot;&gt;ちょっと試す、がすぐにできる&lt;/h3&gt;

&lt;p&gt;本家マニュアルのサンプルがそうですが、Vue.js 本体を script タグで読み込むだけで利用できます。SPA ではなくても、ページの一部に動的な要素を組み込む場合にすぐに使えるので、これだけでも道具箱に入れておくと良いですね。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jp.vuejs.org/v2/guide/index.html#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot;&gt;https://jp.vuejs.org/v2/guide/index.html#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;vue-cli-が楽&quot;&gt;vue-cli が楽&lt;/h3&gt;

&lt;p&gt;まず、何はともあれ開発環境の構築から。これまで経験してきた構成でもそうでしたが、ここに苦労することが多くて、開発にたどり着くまでに手間がかかり、慣れていない構成だと腰が重くなることがありました。&lt;/p&gt;

&lt;p&gt;ただ、時代は進み、現在では、vue-cli や create-react-app などコマンドでベースは自動生成できるようになったので、かなり楽になりました。&lt;/p&gt;

&lt;p&gt;特に vue-cli は良く出来ており、Vue.js 本体だけでなく、vue-router や Vuex、TypeScript などの組み込みも簡単にできました。初期状態では、シンプルですが、ちゃんと vue-router や Vuex を組み込んだアプリケーションも含まれるので、実装サンプルとしても参考になります。&lt;/p&gt;

&lt;pre class=&quot;code lang-sh&quot; data-lang=&quot;sh&quot; data-unlink&gt;$ vue cli sample
Vue CLI v3.1.1
? Please pick a preset: Manually &lt;span class=&quot;synStatement&quot;&gt;select &lt;/span&gt;features
? Check the features needed &lt;span class=&quot;synStatement&quot;&gt;for&lt;/span&gt; your project: &lt;span class=&quot;synPreProc&quot;&gt;(&lt;/span&gt;Press &amp;lt;space&amp;gt; to select, &amp;lt;a&amp;gt; to toggle all, &amp;lt;i&amp;gt; to invert selection&lt;span class=&quot;synPreProc&quot;&gt;)&lt;/span&gt;
❯◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App &lt;span class=&quot;synPreProc&quot;&gt;(&lt;/span&gt;PWA&lt;span class=&quot;synPreProc&quot;&gt;)&lt;/span&gt; Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing
&lt;/pre&gt;


&lt;p&gt;&lt;a href=&quot;https://cli.vuejs.org/&quot;&gt;https://cli.vuejs.org/&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;vue-ファイル&quot;&gt;vue ファイル&lt;/h3&gt;

&lt;p&gt;Vue コンポーネントを記述する .vue ファイルは、3 つのパートで構成されています。パートの指定にタグを使っているので、初見では戸惑ったのですが、意味が分かれば問題ありません。&lt;/p&gt;

&lt;p&gt;React の JSX のように全てが JavaScript で表現できるというのも好きなのですが、これはこれで View / Script / Style が分離できているので分かりやすいです。デザイナさんと協業する場合は、JSX より vue ファイルの方が触ってもらいやすそうですね。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;&amp;lt;template&amp;gt;
  View(HTML)
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
  Config, Logic(JavaScript / TypeScript)
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
  CSS
&amp;lt;/style&amp;gt;&lt;/pre&gt;


&lt;p&gt;ちなみに、vue ファイルは、Single File Component と呼ばれるのですが、これの略称が SFC で、React 界隈で呼ばれる Stateless Functional Component の略称と同じで、はじめは戸惑いました ;)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jp.vuejs.org/v2/guide/single-file-components.html&quot;&gt;https://jp.vuejs.org/v2/guide/single-file-components.html&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;template-ディレクティブ&quot;&gt;template ディレクティブ&lt;/h3&gt;

&lt;p&gt;テンプレートで値のバインドや動的な操作を行いたい場合は、&lt;code&gt;v-if&lt;/code&gt; や &lt;code&gt;v-for&lt;/code&gt; などのディレクティブを利用します。AngularJS を使っていたので、これは特に違和感無いです。&lt;/p&gt;

&lt;p&gt;状態によって、ビューを出し分けたい時、JSX だと、早期リターンが使えたのが楽だったのですが、Vue.js には &lt;code&gt;v-else&lt;/code&gt; があるので、わりとすっきり書けます&lt;a href=&quot;#f-8d644de0&quot; name=&quot;fn-8d644de0&quot; title=&quot;AngularJS だと ng-if が複数並ぶのがウーンという感じでした。&quot;&gt;*1&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;ただ、書き方が AngularJS に似ているので、うっかり &lt;code&gt;ng-if&lt;/code&gt; と書いて、&lt;code&gt;v-if&lt;/code&gt; に書き直すというのも何度もやってしまいます :)&lt;/p&gt;

&lt;h3 id=&quot;scoped-css&quot;&gt;scoped css&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;style scoped&amp;gt;&lt;/code&gt; と宣言するだけで、コンポーネントに閉じた CSS  を適用できます。最終的にどのような形式になるかは別にして、ただ宣言するだけで実現できるというのはホント楽です。&lt;/p&gt;

&lt;p&gt;今回は、bootstrap  を利用したので、全体のスタイルとしてはこれを利用し、差分を style タグ内で指定するという形を取りました。&lt;/p&gt;

&lt;h3 id=&quot;vue-router--Vuex-連携&quot;&gt;vue-router / Vuex 連携&lt;/h3&gt;

&lt;p&gt;SPA  を実装するとなるとルータやストア（ステート）管理が欲しくなります。Vue.js ではこうした機能は別パッケージとなっていますが、公式のパッケージとして扱われており、導入もスムーズに行えます。&lt;/p&gt;

&lt;p&gt;vue-cli でインストールすれば、単にオプションを選択するだけなので、別パッケージということを意識する必要もなく利用できます。このあたりの連携も苦労するポイントだったりするので、簡単に導入できるのは良いですね。&lt;/p&gt;

&lt;p&gt;React のようにそれぞれのパッケージが独立しているのは利点でもあるので、一長一短はありますが、導入が楽なのは Vue.js でした。&lt;/p&gt;

&lt;h3 id=&quot;Vuex&quot;&gt;Vuex&lt;/h3&gt;

&lt;p&gt;Flux アーキテクチャが導入したかったので、Vuex を組み込みました。さらっとドキュメントを見ると、reducer が mutation になっているくらいで概ね同じなのかなと思っていました。&lt;/p&gt;

&lt;p&gt;触った感触では、以下のような違いを感じました。&lt;/p&gt;

&lt;h4 id=&quot;mutation-を直接呼んでstate-を変更&quot;&gt;mutation を直接呼んで、state を変更&lt;/h4&gt;

&lt;p&gt;Redux では、コンポーネントから store を変更する際は、action を発行して、対応する reducer が変更後の store を返し、それが反映されるという流れでした。&lt;/p&gt;

&lt;p&gt;Vuex では、コンポーネントから直接 mutation を呼んで、state を変更するようです。もちろん、action を呼んで、そこから mutation を呼ぶこともできるのですが、非同期処理などが無い場合は、mutation を呼べば ok です。&lt;/p&gt;

&lt;p&gt;サイクルは同じ方向を回るので問題は無いですし、非同期処理などが絡まない場合は action を経由しない分、実装は楽かもしれません。&lt;/p&gt;

&lt;h4 id=&quot;コンポーネントから直接-state-を操作&quot;&gt;コンポーネントから直接 state を操作&lt;/h4&gt;

&lt;p&gt;コンポーネントでは、state を &lt;code&gt;this.$store.state.foo&lt;/code&gt; のように直接参照できます&lt;a href=&quot;#f-cd4a513f&quot; name=&quot;fn-cd4a513f&quot; title=&quot;公式ドキュメントにも記載されている方法です。&quot;&gt;*2&lt;/a&gt;。これは値の読み取りだけでなく、代入も出来てしまうので、やりようによっては、ただのグローバル変数となります。&lt;/p&gt;

&lt;p&gt;このあたりは開発者やチームに自制心が求められるところで、せっかく Flux アーキテクチャのライブラリを採用しているのに、サイクルとは別にあちらこちらで state が変更されると、かえって悲惨なことにも成りかねません。&lt;/p&gt;

&lt;p&gt;コードレビューでコンポーネントからは state を直接参照しない、少なくとも値の変更はしないなどのチェックは必要そうです。lint ツールでこういうチェックができるといいですね。&lt;/p&gt;

&lt;p&gt;mapGetters() や mapActions() のようなヘルパー関数を使って、必要なもののみコンポーネントにマップする方法もあるので、こちらの方が行儀は良さそうです。&lt;/p&gt;

&lt;h3 id=&quot;TypeScript&quot;&gt;TypeScript&lt;/h3&gt;

&lt;p&gt;TypeScript 自体の導入は簡単です。初期設定が終わっていれば、コンポーネントでも  &lt;code&gt;&amp;lt;script lang=&quot;ts&quot;&amp;gt;&lt;/code&gt; を宣言するだけで、その中のスクリプトは TypeScript で書くことができます。&lt;/p&gt;

&lt;p&gt;ただ、テンプレートは TypeScript では無いので、補完や型チェックが不十分だったり、Vuex の連携でもその恩恵が薄かったりします。&lt;/p&gt;

&lt;p&gt;完全な型チェックを求めると不満があるかもしれませんが、JavaScript よりはチェックが機能しますし、導入も簡単なので、TypeScript で書くつもりです。このあたりは、フレームワーク、ライブラリ、ツールの進化で改善していきそうですしね。&lt;/p&gt;

&lt;h3 id=&quot;Vuejs-入門&quot;&gt;Vue.js 入門&lt;/h3&gt;

&lt;p&gt;Vue.js 触るにあたって、&lt;a href=&quot;http://d.hatena.ne.jp/asin/4297100916/shinxblog-22&quot;&gt;Vue.js入門 基礎から実践アプリケーション開発まで&lt;/a&gt; を読みました。これはいいですね。&lt;/p&gt;

&lt;p&gt;この手のフレームワークのサンプルは、1 画面で完成するカウンターや Todo アプリが多く、一覧画面とフォーム画面で構成するようなサンプルは意外と少なかったりします。&lt;/p&gt;

&lt;p&gt;その点、この本では、まさにこうした構成のサンプルアプリケーションを題材として扱っており、完全に自分のニーズにフィットした内容でした。タイトルに「入門」とは付いています&lt;a href=&quot;#f-57b3abd4&quot; name=&quot;fn-57b3abd4&quot; title=&quot;書名を決めるには色々な要素があるので、著者陣の思ったとおりになるとは・・・おっと、誰か来たようだ&quot;&gt;*3&lt;/a&gt;が、開発現場で Vue.js を導入する際に使える内容がふんだんに盛り込まれています。&lt;/p&gt;

&lt;p&gt;Vue.js を試すなら、まずはこの本からはじめるのがオススメです！&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4297100916/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/41qzgSLFQwL._SL160_.jpg&quot; alt=&quot;Vue.js入門 基礎から実践アプリケーション開発まで&quot; title=&quot;Vue.js入門 基礎から実践アプリケーション開発まで&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;そんな感じで、Vue.js を試してみた感触を書いてみました。導入の障壁が低いので、ページの一部から SPA まですぐに試せるのが良いですね。Laravel が採用しているのも分かる気がします :)&lt;/p&gt;

&lt;p&gt;React も良いので、それはそれで継続して使っていこうとは思いますが、ツールの一つとして Vue.js も触っておいて損は無さそうです。React やっていれば、キャッチアップは楽なので、試してみるのも良いですよ。&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-8d644de0&quot; name=&quot;f-8d644de0&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;AngularJS だと ng-if が複数並ぶのがウーンという感じでした。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-cd4a513f&quot; name=&quot;f-cd4a513f&quot; class=&quot;footnote-number&quot;&gt;*2&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;公式ドキュメントにも記載されている方法です。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-57b3abd4&quot; name=&quot;f-57b3abd4&quot; class=&quot;footnote-number&quot;&gt;*3&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;書名を決めるには色々な要素があるので、著者陣の思ったとおりになるとは・・・おっと、誰か来たようだ&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/41qzgSLFQwL._SL160_.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「PHPフレームワーク Laravel Webアプリケーション開発」を執筆しました</title>
        <link href="https://blog.shin1x1.com/entry/laravel-web-application-development"/>
        <id>hatenablog://entry/10257846132628482143</id>
        <published>2018-09-13T11:30:00+09:00</published>
        <updated>2018-09-13T11:30:08+09:00</updated>        <summary type="html">PHPフレームワーク Laravel Webアプリケーション開発 を共著で執筆しました。 @ex_takezawa @kurikazu @shin1x1 @omoon の四人で書いた #Laravel 本がもうすぐ出ます！5.5 LTS 対応で、機能解説だけでなく、利用例を示した実践的な内容になっています。絶賛、予約受付中なので、よろしくお願いしますー！ https://t.co/CnIK6xDhYx— Masashi Shinbara (@shin1x1) September 10, 2018 Laravel 5.5(LTS) に対応した本です。Laravel リファレンス のようにフレーム…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;https://amzn.to/2OaKvmU&quot;&gt;PHPフレームワーク Laravel Webアプリケーション開発 &lt;/a&gt; を共著で執筆しました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4802611846/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51ZkUUB4gqL.jpg&quot; alt=&quot;PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応&quot; title=&quot;PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/ex_takezawa?ref_src=twsrc%5Etfw&quot;&gt;@ex_takezawa&lt;/a&gt; &lt;a href=&quot;https://twitter.com/kurikazu?ref_src=twsrc%5Etfw&quot;&gt;@kurikazu&lt;/a&gt; &lt;a href=&quot;https://twitter.com/shin1x1?ref_src=twsrc%5Etfw&quot;&gt;@shin1x1&lt;/a&gt; &lt;a href=&quot;https://twitter.com/omoon?ref_src=twsrc%5Etfw&quot;&gt;@omoon&lt;/a&gt; の四人で書いた &lt;a href=&quot;https://twitter.com/hashtag/Laravel?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Laravel&lt;/a&gt; 本がもうすぐ出ます！5.5 LTS 対応で、機能解説だけでなく、利用例を示した実践的な内容になっています。絶賛、予約受付中なので、よろしくお願いしますー！ &lt;a href=&quot;https://t.co/CnIK6xDhYx&quot;&gt;https://t.co/CnIK6xDhYx&lt;/a&gt;&lt;/p&gt;&amp;mdash; Masashi Shinbara (@shin1x1) &lt;a href=&quot;https://twitter.com/shin1x1/status/1039134217722974209?ref_src=twsrc%5Etfw&quot;&gt;September 10, 2018&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;p&gt;Laravel 5.5(LTS) に対応した本です。&lt;a href=&quot;https://blog.shin1x1.com/entry/laravel-reference&quot;&gt;Laravel リファレンス&lt;/a&gt; のようにフレームワークの機能解説はもちろん含まれているのですが、機能を紹介するというより、実際にありそうなユースケースでどのように利用するかといった視点で書かれており、開発現場で Laravel をより活用したい向けの内容になっています。目次を見ると、実践的な内容が多く含まれていることが分かるでしょう。&lt;/p&gt;

&lt;p&gt;私は、8 章のコンソールアプリケーションと 9 章のテストを担当しました。&lt;/p&gt;

&lt;p&gt;8 章では、EC サイトなどの購入情報を TSV で出力するコマンドや外部システムへ JSON として送信するバッチ処理を題材に解説しています。9 章では、会員ポイント加算処理を題材にユニットテスト、データベーステスト、WebAPI のテスト（フィーチャテスト）を解説しています。これらの題材となるアプリケーションでは、実装ロジックを Controller や Command に直接実装するのではなく、ユースケースクラスやサービスクラスへ分離するなど、ただフレームワークを使うのではなく、現場でどう活用するかというアイデアを盛り込んでいます。&lt;/p&gt;

&lt;p&gt;本書が、日々の Laravel アプリケーション開発のお役に立てれば嬉しいです。&lt;/p&gt;

&lt;p&gt;絶賛 Amazon で予約受付中ですので、よろしくお願いしますm(&lt;em&gt; &lt;/em&gt;)m&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4802611846/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51ZkUUB4gqL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応&quot; title=&quot;PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4802611846/shinxblog-22/&quot;&gt;PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; 竹澤有貴,栗生和明,新原雅司,大村創太郎,丸山弘詩&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; ソシム&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2018/09/26&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4802611846/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログを見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;h3&gt;目次&lt;/h3&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;1. Laravelの概要
　1-1　Laravelとは
　1-2　環境構築
　1-3　最初のアプリケーション
2. Laravelアーキテクチャ
　2-1　ライフサイクル
　　2-1-1　Laravelアプリケーション実行の流れ
　　2-1-2　エントリポイント
　　2-1-3　HTTPカーネル
　　2-1-4　ルータ
　　2-1-5　ミドルウェア
　　2-1-6　コントローラ
　2-2　サービスコンテナ
　　2-2-1　サービスコンテナとは
　　2-2-2　バインドと解決
　　2-2-3　バインド
　　2-2-4　解決
　　2-2-5　DIとサービスコンテナ
　　2-2-6　ファサード
　2-3　サービスプロバイダ
　　2-3-1　サービスプロバイダの基本的な動作
　　2-3-2　deferプロパティによる遅延実行
　2-4　コントラクト
　　2-4-1　コントラクトの基本
　　2-4-2　コントラクトを利用した機能の差し替え
3. アプリケーションアーキテクチャ
　3-1　MVCとADR
　　3-1-1　MVC（Model View Controller）
　　3-1-2　ADR（Action Domain Responder）
　3-2　アーキテクチャへの入口
　　3-2-1　フレームワークとアーキテクチャ設計
　　3-2-2　アーキテクチャ設計のポイント
　　3-2-3　レイヤードアーキテクチャ
　　3-2-4　レイヤードアーキテクチャの一歩先の世界
4. HTTPリクエストとレスポンス
　4-1　リクエストハンドリング
　　4-1-1　リクエストの取得
　　4-1-2　Inputファサード・Requestファサード
　　4-1-3　Requestオブジェクト
　　4-1-4　フォームリクエスト
　4-2　バリデーション
　　4-2-1　バリデーションルールの指定方法
　　4-2-2　バリデーションルール
　　4-2-3　バリデーションの利用
　　4-2-4　バリデーション失敗時の処理
　　4-2-5　ルールのカスタマイズ
　4-3　レスポンス
　　4-3-1　さまざまなレスポンス
　　4-3-2　リソースクラスを組み合わせたREST APIレスポンスパターン
　4-4　ミドルウェア
　　4-4-1　ミドルウェアの基本
　　4-4-2　デフォルトで用意されているミドルウェア
　　4-4-3　独自ミドルウェアの実装
5. データベース
　5-1　マイグレーション
　　5-1-1　マイグレーション処理の流れ
　　5-1-2　マイグレーションファイルの作成
　　5-1-3　定義の記述
　　5-1-4　マイグレーションの実行とロールバック
　5-2　シーダー
　　5-2-1　シーダーの作成
　　5-2-2　シーダークラスを利用するための設定
　　5-2-3　シーディングの実行
　　5-2-4　Fakerの利用
　　5-2-5　Factoryを利用する例
　5-3　Eloquent
　　5-3-1　クラスの作成
　　5-3-2　規約とプロパティ
　　5-3-3　データ検索・データ更新の基本
　　5-3-4　データ操作の応用
　　5-3-5　関連性を持つテーブル群の値をまとめて操作する（リレーション）
　　5-3-6　実行されるSQLの確認
　5-4　クエリビルダ
　　5-4-1　クエリビルダの書式
　　5-4-2　クエリビルダの取得
　　5-4-3　処理対象や内容の特定
　　5-4-4　クエリの実行
　　5-4-5　トランザクションとテーブルロック
　　5-4-6　ベーシックなデータ操作
　5-5　リポジトリパターン
　　5-5-1　リポジトリパターンの概要
　　5-5-2　リポジトリパターンの実装
6. 認証と認可
　6-1　セッションを利用した認証
　　6-1-1　認証を支えるクラスとその機能
　　6-1-2　認証処理を理解する
　　6-1-3　データベース・セッションによる認証処理
　　6-1-4　フォーム認証への適用
　　6-1-5　認証処理のカスタマイズ
　　6-1-6　パスワードリセット
　6-2　トークン認証
　　6-2-1　api_tokenを保持するテーブルの作成
　　6-2-2　シーダーを用いたレコードの作成
　　6-2-3　独自認証プロバイダの作成
　　6-2-4　トークン認証の利用方法
　6-3　JWT認証
　　6-3-1　tymon/jwt-authのインストール
　　6-3-2　tymon/jwt-authの利用準備
　　6-3-3　tymon/jwt-authの利用方法
　　6-3-4　トークンの発行
　6-4　OAuthクライアントによる認証・認可
　　6-4-1　Socialite
　　6-4-2　GitHub OAuth認証
　　6-4-3　動作拡張
　　6-4-4　OAuthドライバの追加
　6-5　認可処理
　　6-5-1　認可処理を理解する
　　6-5-2　認可処理
　　6-5-3　Bladeテンプレートによる認可処理
7. 処理の分離
　7-1　イベント
　　7-1-1　イベントの基本
　　7-1-2　イベントの作成
　　7-1-3　イベントを利用した堅実なオブザーバーパターン
　　7-1-4　イベントのキャンセル
　　7-1-5　非同期イベントを利用する分離パターン
　7-2　キュー
　　7-2-1　キューの基本
　　7-2-2　非同期実行ドライバの準備（Queueドライバ）
　　7-2-3　キューの仕様
　　7-2-4　キューによるPDFファイル出力パターン
　　7-2-5　Supervisorによる常駐プログラムパターン
　　7-2-6　手軽な分散処理パターン
　7-3　イベントとキューによるCQRS
　　7-3-1　CQRS（コマンドクエリ責務分離）
　　7-3-2　アプリケーション仕様
　　7-3-3　アプリケーション実装の準備
　　7-3-4　口コミ登録機能の実装
　　7-3-5　口コミ投稿コントローラ実装
　　7-3-6　リスナークラスによるElasticsearch操作
　　7-3-7　Commandの実行・Queryの実装
8. コンソールアプリケーション
　8-1　Commandの基礎
　　8-1-1　クロージャによるCommandの作成
　　8-1-2　クラスによるCommandの作成
　　8-1-3　Commandへの入力
　　8-1-4　Commandからの出力
　　8-1-5　Commandの実行
　8-2　Commandの実装
　　8-2-1　サンプル実装の仕様
　　8-2-2　Commandの生成
　　8-2-3　ユースケースクラスとサービスクラスの分離
　　8-2-4　ユースケースクラスの雛形を作成する
　　8-2-5　サービスクラスの実装
　　8-2-6　ユースケースクラスの実装
　　8-2-7　Commandクラスの仕上げ
　8-3　バッチ処理の実装
　　8-3-1　バッチ処理の仕様
　　8-3-2　Commandクラスの実装
　　8-3-3　ユースケースクラスの実装
　　8-3-4　Commandクラスの仕上げ
　　8-3-5　バッチ処理のログ出力
　　8-3-6　チャットサービスへの通知（チャットワーク）
9. テスト
　9-1　ユニットテスト
　　9-1-1　テスト対象クラス
　　9-1-2　テストクラスの生成
　　9-1-3　テストメソッドの実装
　　9-1-4　データプロバイダの活用
　　9-1-5　例外のテスト
　　9-1-6　テストの前処理・後処理
　　9-1-7　テストの設定
　9-2　データベーステスト
　　9-2-1　テスト対象のテーブルとクラス
　　9-2-2　データベーステストの基礎
　　9-2-3　Eloquentクラスのテスト
　　9-2-4　サービスクラスのテスト
　　9-2-5　モックによるテスト（サービスクラス）
　9-3　WebAPIテスト
　　9-3-1　WebAPIテスト機能
　　9-3-2　テスト対象のAPI
　　9-3-3　APIテストの実装
　　9-3-4　WebAPIテストに便利な機能
10. アプリケーション運用
　10-1　エラーハンドリング
　　10-1-1　エラー表示
　　10-1-2　エラーの種別
　　10-1-3　エラーハンドリングの基本
　　10-1-4　Fluentdの活用
　　10-1-5　例外の描画テンプレート変更
　　10-1-6　エラーハンドリングパターン
　10-2　ログ活用パターン
　　10-2-1　ログの基本
　　10-2-2　ログ出力設定
　　10-2-3　権限によるログファイル分離方法
　　10-2-4　カスタムログドライバの実装
　　10-2-5　Laravel 5.6でのElasticsearchログドライバ
　　10-2-6　Laravel 5.5でのElasticsearchログドライバ
11. テスト駆動開発の実践
　11-1　テスト駆動開発とは
　　11-1-1　コツはできるだけ小さく
　　11-1-2　サンプルアプリケーション仕様
　　11-1-3　データベース仕様
　　11-1-4　APIエンドポイント
　11-2　APIエンドポイントの作成
　　11-2-1　アプリケーションの作成・事前準備
　　11-2-2　最初のテスト
　　11-2-3　テストメソッドに何をどのように書くか
　　11-2-4　最低限の実装
　　11-2-5　2つ目以降のテスト
　　11-2-6　1つのテストメソッドに検証は1つの原則
　　11-2-7　テストコードの確認
　11-3　テストに備えるデータベース設定
　　11-3-1　データベース設定
　　11-3-2　マイグレーション・モデル・ファクトリ
　　11-3-3　初期データ投入用シーダーの準備
　11-4　データベーステスト
　　11-4-1　テスト用トレイトの利用・初期データの投入
　　11-4-2　データベースが絡むテスト
　　11-4-3　仮実装で素早くテストを成功させる
　　11-4-4　最初のリファクタリング
　　11-4-5　返却値の内容を検証
　　11-4-6　成功が分かっているテストの追加
　　11-4-7　データ追加の検証
　　11-4-8　既存テストの修正
　　11-4-9　バリデーションテスト
　11-5　リファクタリングユースケース
　　11-5-1　そろそろコントローラを使う
　　11-5-2　フレームワークの標準に寄せていくリファクタリング①
　　11-5-3　正確なテストが書けない時の対処法
　　11-5-4　フレームワークの標準に寄せていくリファクタリング②
　　11-5-5　サービスクラスへの分離&lt;/pre&gt;

</content>        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/51ZkUUB4gqL.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>ab ライクに SQS にエンキューするツール en9sqs</title>
        <link href="https://blog.shin1x1.com/entry/en9sqs"/>
        <id>hatenablog://entry/10257846132596274795</id>
        <published>2018-06-29T13:17:57+09:00</published>
        <updated>2018-06-29T13:17:57+09:00</updated>        <summary type="html">SQS から AWS Lambda をトリガーできるようになりましたね！ ワーカーを AWS Lambda で動かせるので、これは嬉しいリリースです。 aws.amazon.com en9sqs こういった機能を試すには、SQS にメッセージをエンキューする必要があるわけで、 パフォーマンスを見たい時は大量にメッセージを投下したい場合もあったりします。 そんな時に便利なのが、en9sqs というツールです。 golang で書いており、並列にメッセージをエンキューできます。 github.com インストール インストールするには、go get を使うか、64bit macOS 用ならバイナリ…</summary>
        <content type="html">&lt;p&gt;SQS から AWS Lambda をトリガーできるようになりましたね！
ワーカーを AWS Lambda で動かせるので、これは嬉しいリリースです。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Faws%2Faws-lambda-adds-amazon-simple-queue-service-to-supported-event-sources%2F&quot; title=&quot;AWS Lambda Adds Amazon Simple Queue Service to Supported Event Sources | Amazon Web Services&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/jp/blogs/aws/aws-lambda-adds-amazon-simple-queue-service-to-supported-event-sources/&quot;&gt;aws.amazon.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;en9sqs&lt;/h2&gt;

&lt;p&gt;こういった機能を試すには、SQS にメッセージをエンキューする必要があるわけで、
パフォーマンスを見たい時は大量にメッセージを投下したい場合もあったりします。&lt;/p&gt;

&lt;p&gt;そんな時に便利なのが、en9sqs というツールです。
golang  で書いており、並列にメッセージをエンキューできます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Fen9sqs&quot; title=&quot;shin1x1/en9sqs&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/en9sqs&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h3&gt;インストール&lt;/h3&gt;

&lt;p&gt;インストールするには、go get を使うか、64bit macOS 用ならバイナリが releases にあるので、これをダウンロードするのが良いでしょう。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ go get -u github.com/shin1x1/en9sqs&lt;/pre&gt;


&lt;p&gt;&lt;a href=&quot;https://github.com/shin1x1/en9sqs/releases&quot;&gt;Releases &amp;middot; shin1x1/en9sqs &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;実行例&lt;/h3&gt;

&lt;p&gt;下記が実行例です。ab（Apache Bench）と同様に、-c が並列度で、-n がメッセージ総数を指定します。その後に、キューの URL、そして送信するメッセージを指定します。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;# 1,000 件のメッセージを 10 並列で送信
$ en9sqs -c 10 -n 1000 https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/hogehoge &amp;#34;Hello&amp;#34;
2018/06/29 13:15:22 message 100 done(worker:5)
2018/06/29 13:15:22 message 200 done(worker:3)
2018/06/29 13:15:22 message 300 done(worker:7)
2018/06/29 13:15:23 message 400 done(worker:10)
2018/06/29 13:15:23 message 500 done(worker:3)
2018/06/29 13:15:23 message 600 done(worker:5)
2018/06/29 13:15:24 message 700 done(worker:4)
2018/06/29 13:15:24 message 800 done(worker:8)
2018/06/29 13:15:24 message 900 done(worker:1)
2018/06/29 13:15:25 message 1000 done(worker:10)
2018/06/29 13:15:25 Done(1000 messages)&lt;/pre&gt;


&lt;p&gt;ツールとして、並列数やメッセージ数に制限はかけていないですが、あまりに並列数を上げすぎると SQS がエラーを返すようになるので注意して下さい。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;golang だとこういった並列で何かをするツールをサクッと書けるので良いですね！
機能としては、まだ追加したいもの（メッセージのテンプレート化、sleep 機能など）もあるので、ぼちぼち追加していきます。もちろん、PR も歓迎ですー。&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>独立したコアレイヤパターン</title>
        <link href="https://blog.shin1x1.com/entry/independent-core-layer-pattern"/>
        <id>hatenablog://entry/17391345971643187515</id>
        <published>2018-05-11T00:55:05+09:00</published>
        <updated>2018-05-21T20:31:02+09:00</updated>        <summary type="html">モチベーション 全体 サンプルアプリケーション コアレイヤ サービスレイヤ 口座間送金ユースケース 処理の流れ コアレイヤ サービスレイヤ コアレイヤ対象範囲 DDD スタイル 手続き型スタイル 実装アイデア レイヤでパッケージを分ける コアレイヤの範囲 ポートの種類 DDD スタイルへの一歩目 さいごに 参考 独立したコアレイヤは、アプリケーション実装パターンである。以下のような特徴を持つ。 アプリケーションを、何を実現するのか（What）と、どのように実現するのか（How）に分ける。 What は、コアレイヤに実装する。ユースケースやドメインロジックを実装する。フレームワークやライブラリに…</summary>
        <content type="html">&lt;ul class=&quot;table-of-contents&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;#モチベーション&quot;&gt;モチベーション&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#全体&quot;&gt;全体&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#サンプルアプリケーション&quot;&gt;サンプルアプリケーション&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#コアレイヤ&quot;&gt;コアレイヤ&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#サービスレイヤ&quot;&gt;サービスレイヤ&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#口座間送金ユースケース&quot;&gt;口座間送金ユースケース&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#処理の流れ&quot;&gt;処理の流れ&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#コアレイヤ-1&quot;&gt;コアレイヤ&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#サービスレイヤ-1&quot;&gt;サービスレイヤ&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#コアレイヤ対象範囲&quot;&gt;コアレイヤ対象範囲&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#DDD-スタイル&quot;&gt;DDD スタイル&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#手続き型スタイル&quot;&gt;手続き型スタイル&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#実装アイデア&quot;&gt;実装アイデア&lt;/a&gt;&lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;#レイヤでパッケージを分ける&quot;&gt;レイヤでパッケージを分ける&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#コアレイヤの範囲&quot;&gt;コアレイヤの範囲&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#ポートの種類&quot;&gt;ポートの種類&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;#DDD-スタイルへの一歩目&quot;&gt;DDD スタイルへの一歩目&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#さいごに&quot;&gt;さいごに&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;独立したコアレイヤは、アプリケーション実装パターンである。以下のような特徴を持つ。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;アプリケーションを、何を実現するのか（What）と、どのように実現するのか（How）に分ける。&lt;/li&gt;
&lt;li&gt;What は、コアレイヤに実装する。ユースケースやドメインロジックを実装する。フレームワークやライブラリには依存しない。UI やデータベースからは独立している。&lt;/li&gt;
&lt;li&gt;How は、サービスレイヤ（仮）に実装する。フレームワークやライブラリを活用して、ユースケースが要求する技術詳細を実装する。&lt;/li&gt;
&lt;li&gt;コアレイヤが必要な技術詳細はポート（インタフェース）で定義し、これに依存する。サービスレイヤでは、それに対するアダプタを実装する。&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;ここでは、Web アプリケーションを念頭においているが、内容は Web アプリケーションに特化するものではなく、他種のアプリケーションでも適用可能と考える。&lt;/p&gt;

&lt;h2 id=&quot;モチベーション&quot;&gt;モチベーション&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;技術詳細からの独立&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;コアレイヤでは、ユースケースやドメインを記述するが、UI、フレームワークやサードパーティライブラリ、外部ミドルウェア通信等の詳細技術とは独立しているので、処理の流れやドメインロジックといった何を実現するのかを平易に記述していくだけで良い。こうしたコードは、後に他の開発者がコードを見た時にもアプリケーションが実現したいことが把握しやすい。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;フレームワーク、サードパーティライブラリからの独立&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;コアレイヤは、フレームワークやサードパーティライブラリには依存しない。こうした機能は、ポート経由で間接的に利用する。あくまでコアレイヤは、ポートに依存しているだけでその詳細には関与しない。こうしておくことで、フレームワークやライブラリのバージョンアップや入れ替えが発生しても、その影響はサービスレイヤで実装するアダプタに留まり、コアレイヤには及ばない。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;サービスレイヤからの独立&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;コアレイヤは、サービスレイヤにも依存しないので、単体のアプリケーションとして利用することも可能である。つまり、テストランナーを使えば、単体でテストを実行できる。また、異なるサービスレイヤ、例えば Web アプリケーションとバッチアプリケーションがあっても、同じコアレイヤを使うことができる。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;コアレイヤからの独立&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;サービスレイヤについても、コアレイヤから独立していることで、要求される技術に特化した処理を実装すれば良くなる。HTTP リクエストのハンドリング、レスポンス生成、データベースアクセス、メール送信といったものだ。こうしたものは、汎用フレームワークやライブラリで提供されているので、それらをアダプタとして、コアレイヤに提供すれば良い。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;シンプルなルール&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;本パターンで定めているのは、レイヤをコアレイヤとサービスレイヤの 2 つに分ける、コアレイヤは外部の技術詳細に依存せずに自身で定義したポートに依存する、だけである。&lt;/p&gt;

&lt;p&gt;このシンプルなルールにより、小さなアプリケーションにも適用しやすいものとなっている。&lt;/p&gt;

&lt;h2 id=&quot;全体&quot;&gt;全体&lt;/h2&gt;

&lt;p&gt;下記に独立したコアレイヤパターンのイメージ図を示す。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20180510/20180510235928.png&quot; alt=&quot;f:id:shin1x1:20180510235928p:plain&quot; title=&quot;f:id:shin1x1:20180510235928p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;この図を見れば、ヘキサゴナルアーキテクチャやオニオンアーキテクチャ、クリーンアーキテクチャを想起するだろう。本パターンは、ヘキサゴナルアーキテクチャ（ Ports and Adapters パターン）の一種である。コアレイヤを中心におき、外部にある実装はインタフェース経由で利用する。&lt;/p&gt;

&lt;p&gt;前述したアーキテクチャと異なるのは、レイヤ構造を 2 つにしている点とポートの種類や役割を限定していないことである。例えば、ヘキサゴナルアーキテクチャでは、ポートをプライマリとセカンダリに区分している。細かな違いではあるが、定義を明確にするために新たな名称を付けた。&lt;/p&gt;

&lt;h2 id=&quot;サンプルアプリケーション&quot;&gt;サンプルアプリケーション&lt;/h2&gt;

&lt;p&gt;本エントリで提示するコードは、下記リポジトリにある。Docker 開発環境を構築できるので、テストなど動かしてみると良いだろう。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Findependent-core-layer-laravel&quot; title=&quot;shin1x1/independent-core-layer-laravel&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/independent-core-layer-laravel&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2 id=&quot;コアレイヤ&quot;&gt;コアレイヤ&lt;/h2&gt;

&lt;p&gt;コアレイヤは、ユースケースとユースケースが利用するサービスのポート（インタフェース）を実装する。ユースケースは、想定される処理を順に記述していく。データベースアクセスなど技術詳細を利用したい場合、必要な API をインタフェースとして実装して、それに依存しておく。ユースケース内では、このインタフェースを利用して値の取得や保存などの処理を行う。&lt;/p&gt;

&lt;p&gt;アプリケーションドメインに関する実装、エンティティや ValueObject、ドメインサービスなど、もコアレイヤに含める。ただ、これは必須ではなく、シンプルなユースであればこうしたドメインモデルを使わずにユースケースを実装する場合もある。&lt;/p&gt;

&lt;p&gt;このレイヤに技術詳細は含まないので、Web アプリケーションフレームワークやサードパーティライブラリなどには依存しない。ただし、データやアルゴリズムなどを提供するライブラリについては依存する場合がある。例えば、PHP であれば、日付時間ライブラリである Carbon や Chronos 、データ構造ライブラリの php-ds&lt;a href=&quot;#f-68044b2a&quot; name=&quot;fn-68044b2a&quot; title=&quot;http://php.net/manual/ja/book.ds.php&quot;&gt;*1&lt;/a&gt; などの依存は考えられる。&lt;/p&gt;

&lt;p&gt;以下に一つの例を示す。ここでは、銀行口座の情報を取得するユースケースを実装している。&lt;/p&gt;

&lt;p&gt;なお、本エントリ全体で例示するソースコードは、シンプルな銀行口座アプリケーションのものである。また、AccountNumber や Account などはドメインモデルとして、このレイヤに含まれているとする。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Account\UseCase\GetAccount;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Exceptions\NotFoundException;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\Account;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\AccountNumber;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; GetAccount
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;GetAccountQueryPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; GetAccountQueryPort
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;GetAccount クラスがユースケース、GetAccountQueryPort インタフェースがこのユースケースが利用するポートである。ユースケースでは、コンストラクタにポートを引数で受け取る。execute メソッドにユースケースを実装する。このメソッドでは、単に GetAccountQueryPort インタフェースの findAccount メソッドを実行してその戻り値を返すだけである。&lt;/p&gt;

&lt;p&gt;execute メソッドを見れば、このユースケースが何をするかは一目瞭然である。たが、実際に口座情報がどこにあり、それをどのように行うかはコアレイヤは知らない。&lt;/p&gt;

&lt;p&gt;ポートには、ユースケースごとに定義する。読み取り処理を担う Query ポート、データ保存やメール送信など副作用を伴う処理を担う Command ポート、アプリケーション全体で横断的に利用する Transaction ポートなど考えられる。ただ、これは本パターンで規定しておらず、あくまで一例である。&lt;/p&gt;

&lt;p&gt;ユースケースはフレームワークやサードパーティライブラリには依存せず、データベースなどにも直接接続しないので、テスト用データベースのセットアップなどなくても簡単にテストが書ける。以下が GetAccount ユースケースのテストである。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Test\Account\UseCase;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Exceptions\NotFoundException;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\Account;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\AccountNumber;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\UseCase\GetAccount\GetAccount;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\UseCase\GetAccount\GetAccountQueryPort;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; PHPUnit\Framework\TestCase;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; GetAccountTest &lt;span class=&quot;synType&quot;&gt;extends&lt;/span&gt; TestCase
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;@test&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@throws &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;NotFoundException&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; execute&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sut&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; GetAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt; GetAccountQueryPort
            &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account
                &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; Account&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;ofByArray&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;
                        &#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;,
                        &#39;&lt;span class=&quot;synConstant&quot;&gt;email&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;a@example.com&lt;/span&gt;&#39;,
                        &#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synConstant&quot;&gt;1000&lt;/span&gt;,
                    &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
                &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;A0001&lt;/span&gt;&#39;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sut&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;assertSame&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asString&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;assertSame&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1000&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;@test&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;@expectedException &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;\Acme\Account\Domain\Exceptions\NotFoundException&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; error_account_not_found&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sut&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; GetAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt; GetAccountQueryPort
            &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account
                &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; NotFoundException&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
                &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sut&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;Z9999&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;GetAccountQueryPort をインジェクトするだけでユースケースは実行できるので、無名クラスでこのインタフェースを実装して与えている。GetAccountTest クラスの execute メソッドでは正常ケースとして、Account インスタンスを返している。一方、error_account_not_found メソッドでは、口座情報が見つからないケースとして例外を送出している。このようにユースケースではインタフェースに依存しているだけなので、インジェクトするクラスの実装をいかようにも変えることができる。&lt;/p&gt;

&lt;p&gt;このユースケースは、あまりにシンプルでイメージが沸かないということであれば、後述する口座間送金ユースケースの例を見ると良い。&lt;/p&gt;

&lt;h2 id=&quot;サービスレイヤ&quot;&gt;サービスレイヤ&lt;/h2&gt;

&lt;p&gt;サービスレイヤは、UI や データベースなどアプリケーション外部との連携を実装する。&lt;/p&gt;

&lt;p&gt;このレイヤでは、2つの責務を担う。1 つ目は、UI からのアクションを契機にユースケースを実行することである。コアレイヤから提供されるユースケースクラスを実行することになる。2 つ目は、実行するユースケースが依存しているポートに対するアダプタを実装することだ。インタフェースを満たせば、その実装の詳細は自由である。&lt;/p&gt;

&lt;p&gt;先の GetAccount ユースケースに対する本レイヤの実装を見ていこう。ここでは、Laravel 5.6 を利用しているものとする。&lt;/p&gt;

&lt;p&gt;まず、GetAccount ユースケースが要求している GetAccountQueryPort に対するアダプタ実装が以下となる。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; App\Action\GetAccount;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Exceptions\NotFoundException;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\Account;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\AccountNumber;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\UseCase\GetAccount\GetAccountQueryPort;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; App\Eloquents\EloquentAccount;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; GetAccountAdapter &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt; GetAccountQueryPort
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;EloquentAccount &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;account &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;account&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findByAccountNumber&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;is_null&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; NotFoundException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number %s not found&lt;/span&gt;&#39;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;__toString&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;()))&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;toModel&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;GetAccountAdapter クラスでは、GetAccountQueryPort を実装している。内部では、Eloquent を利用して、データベースから口座情報を取得し、Account インスタンスとして返している。ここでは、コンストラクタで EloquentAccount をインジェクトしているが、ファサードを使っても問題無い。サービスレイヤでは、フレームワークの機能を利用して、より開発しやすい方法で実装すれば良い。&lt;/p&gt;

&lt;p&gt;Laravel にはサービスコンテナという DI コンテナがあるので、GetAccount クラス生成時にこのアダプタを与えるように定義しておく。ここでは、CoreServiceProvider を追加して、その中で DI コンテナに定義を追加している。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; App\Providers;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\UseCase\GetAccount\GetAccount;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; App\Action\GetAccount\GetAccountAdapter;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; CoreServiceProvider &lt;span class=&quot;synType&quot;&gt;extends&lt;/span&gt; ServiceProvider
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; boot&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synComment&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; register&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;app&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;bind&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;GetAccount&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt;, &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;adapter&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;GetAccountAdapter&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; GetAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;})&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;Laravel のルーティングからユースケースを実行する GetAccountAction は以下になる。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; App\Action\GetAccount;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\Domain\Models\AccountNumber;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Acme\Account\UseCase\GetAccount\GetAccount;
&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Illuminate\Http\Request;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; GetAccountAction
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;useCase&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;GetAccount &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;useCase&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;useCase &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;useCase&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; __invoke&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;Request &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;request&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;useCase&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;json&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;
            &#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asString&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
        &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;コンストラクタで、先程 CoreServiceProvider でサービスコンテナに登録した GetAccount インスタンスが与えられる。ルーティングからは、__invoke メソッドが実行される。この中でパラメータのバリデーションなどを行って、ユースーケースを実行する。HTTP に関することは、このクラスで処理して、ユースーケースには渡さないようにする。ユースーケースからは、Account インスタンスが返されるので、これを JSON で出力している。&lt;/p&gt;

&lt;p&gt;このように、アプリケーション外部やフレームワークなどに関する処理は全てこのレイヤで行う。&lt;/p&gt;

&lt;h2 id=&quot;口座間送金ユースケース&quot;&gt;口座間送金ユースケース&lt;/h2&gt;

&lt;p&gt;より実際のアプリケーションに近いかたちとして、口座間の送金処理ユースケースの実装例を見る。&lt;/p&gt;

&lt;h3 id=&quot;処理の流れ&quot;&gt;処理の流れ&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;送金元口座番号、送金先口座番号、送金金額を指定する。&lt;/li&gt;
&lt;li&gt;送金元口座番号から送金元口座情報を取得する。&lt;/li&gt;
&lt;li&gt;送金先口座番号から送金先口座情報を取得する。&lt;/li&gt;
&lt;li&gt;送金元口座から送金金額を出金する。&lt;/li&gt;
&lt;li&gt;送金先口座に送金金額を入金する。&lt;/li&gt;
&lt;li&gt;送金元口座の取引ログに出金を記録する。&lt;/li&gt;
&lt;li&gt;送金先口座の取引ログに入金を記録する。&lt;/li&gt;
&lt;li&gt;送金元口座残高を保存する。&lt;/li&gt;
&lt;li&gt;送金先口座残高を保存する。&lt;/li&gt;
&lt;/ul&gt;


&lt;h3 id=&quot;コアレイヤ-1&quot;&gt;コアレイヤ&lt;/h3&gt;

&lt;p&gt;ユースケース TransferMoney クラスは以下のようになる。&lt;/p&gt;

&lt;p&gt;まず、3 つのポートに依存している。TransferMoneyQueryPort はデータ取得のような読み取り処理を担うポート、TransferMoneyCommandPort はデータ保存やメール送信のような副作用を伴う処理を担うポートTransactionPort は、データベーストランザクションを担う汎用ポートである。&lt;/p&gt;

&lt;p&gt;execte メソッドでは、送信元口座番号、送信先口座番号、送金金額、そして取引ログを記録するために処理日時を引数に取る。これは、サービスレイヤから与えられる。このメソッドでは、Query ポートで口座情報取得、ドメインルール検証、ドメインロジック（送金）、Command ポートで結果の保存、通知といった処理を行っている。一連の処理は、Transaction ポートでトランザクション内で実行するようにしている。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;// (snip)&lt;/span&gt;
&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; TransferMoney
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        TransferMoneyQueryPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;,
        TransferMoneyCommandPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;,
        TransactionPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;,
        AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;,
        Money &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Balance &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;equals&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; DomainRuleException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;source can not transfer to same account&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;lessThan&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;source account does not have enough balance for transfer %s&lt;/span&gt;&#39;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
                &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; DomainRuleException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;withdraw&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;deposit&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;notify&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;})&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;query メソッドでは、Query ポートを使って、口座番号から送金元、送信先口座情報を取得している。このユースケースでは後ほど、残高を更新するためにレコードロックを獲得している。この時、デッドロックを防ぐために口座番号の若い方からロックをかけるようにしている。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; query&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;lessThan&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;store メソッドでは、入金後の口座残高を保存している。また、送金処理に関する取引ログも合わせて記録している。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;Account &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, Account &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;, Money &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, TransactionTime &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;storeBalance&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;storeBalance&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;addTransaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; Transaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
            TransactionType&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;WITHDRAW&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transferred to &lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asString&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;addTransaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; Transaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
            TransactionType&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;DEPOSIT&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;,
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transferred from &lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;accountNumber&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asString&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;この実装を見て分かるようにコアレイヤでは、トランザクションやレコードロックの順序などいわゆるドメインレイヤでは扱わないコードについても対象となり得る。どこまでをコアレイヤにすべきかということは本パターンでは特に定めない。&lt;/p&gt;

&lt;h3 id=&quot;サービスレイヤ-1&quot;&gt;サービスレイヤ&lt;/h3&gt;

&lt;p&gt;サービスレイヤでは、ユースケースの実行と定義されたポートに対するアダプタを実装していく。&lt;/p&gt;

&lt;p&gt;GetAccount と同様にルーティングに設定されたアクションでは下記のようにユースケースを実行している。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; __invoke&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;TransferMoneyRequest &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;request&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;validated&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;validated&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;balance&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;useCase&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
            AccountNumber&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;,
            AccountNumber&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;validated&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;destination_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;,
            Money&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;of&lt;span class=&quot;synSpecial&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;validated&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;money&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;,
            TransactionTime&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;now&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;json&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;
            &#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;asInt&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;,
        &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;ポートに対するアダプタは下記のようになる。
&lt;a href=&quot;https://github.com/shin1x1/independent-core-layer-laravel/blob/master/app/Action/TransferMoney/TransferMoneyAdapter.php&quot;&gt;https://github.com/shin1x1/independent-core-layer-laravel/blob/master/app/Action/TransferMoney/TransferMoneyAdapter.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ここでは、Query ポートと Command ポートを同じアダプタクラスで実装している。ユースケースに対するポート実装が小さいものなら、こうした実装方法も考えられる。ポートにマッチするアダプタが提供されれば良いので、どのようにアダプタを実装するかは、本パターンでは定めない。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; TransferMoneyAdapter &lt;span class=&quot;synType&quot;&gt;implements&lt;/span&gt;
    TransferMoneyQueryPort,
    TransferMoneyCommandPort
&lt;/pre&gt;


&lt;h2 id=&quot;コアレイヤ対象範囲&quot;&gt;コアレイヤ対象範囲&lt;/h2&gt;

&lt;p&gt;本パターンを実装する上で悩むところは、コアレイヤにどこまでを含めるかという点であろう。コアレイヤに技術詳細を含めないことは当然なのだが、どこまで抽象化するかは実装者に委ねられる。それを考えるヒントとして、TransferMoney の異なる実装を 2 つ提示する。&lt;/p&gt;

&lt;h3 id=&quot;DDD-スタイル&quot;&gt;DDD スタイル&lt;/h3&gt;

&lt;p&gt;DDD パターンを活用して、よりドメインレイヤに近いかたちで実装したのが以下の例だ。ここでは、TransferMoneyAggregate という集約を利用して、Query や Command は、この集約単位で行っている。また、ドメインルール検証も TransferMoneySpec で実装している。&lt;/p&gt;

&lt;p&gt;execute メソッドを見るとかなりシンプルな実装となっていること分かる。ただ、上記であったレコードロックの順番に関する実装は、サービスレイヤに任せるようになっているので、コアレイヤをやや小さくしたパターンともいえる。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;snip&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; DDDStyleTransferMoney
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        DDDStyleTransferMoneyQuery &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;,
        DDDStyleTransferMoneyCommandPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;,
        TransactionPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;,
        AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;,
        Money &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
        TransactionTime &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Balance &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; TransferMoneySpec&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;isSatisfiedBy&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; DomainRuleException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;TransferMoneySpec is not satisfied.&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transfer&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;notify&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;source&lt;span class=&quot;synSpecial&quot;&gt;())&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;balance&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;})&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; DDDStyleTransferMoneyQuery
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; find&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; TransferMoneyAggregate;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;AccountNumber &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; Account;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; DDDStyleTransferMoneyCommandPort
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;TransferMoneyAggregate &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; notify&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;Account &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;h3 id=&quot;手続き型スタイル&quot;&gt;手続き型スタイル&lt;/h3&gt;

&lt;p&gt;次は、手続き型によるユースケースの実装を見る。ここでは、ドメインモデルは一切利用せずに string や int、array といったプリミティブな型のみで実装している。ポートでの API 定義もプリミティブな型のみ利用しているので、アダプタも array を返すような実装となる。本パターンでは、このような手続き型スタイルで実装することも可能である。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;snip&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; ProcedureStyleTransferMoney
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synStatement&quot;&gt;const&lt;/span&gt; TRANSACTION_TYPE_WITHDRAW &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;WITHDRAW&lt;/span&gt;&#39;;
    &lt;span class=&quot;synStatement&quot;&gt;const&lt;/span&gt; TRNSACTION_TYPE_DEPOSIT &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;DEPOSIT&lt;/span&gt;&#39;;

    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        ProcedureStyleTransferMoneyQueryPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;,
        ProcedureStyleTransferMoneyCommandPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;,
        TransactionPort &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;query&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;command&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; execute&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;,
        &lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;,
        &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
        Chronos &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;transaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; DomainRuleException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;source can not transfer to same account&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;source account does not have enough balance for transfer %s&lt;/span&gt;&#39;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
                &lt;span class=&quot;synStatement&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; DomainRuleException&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;notify&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;})&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; query&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destinationNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;query&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;sourceNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; store&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;, Chronos &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;storeBalance&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;storeBalance&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;balance&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;

        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;addTransaction&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;
            &#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transaction_type&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;TRANSACTION_TYPE_WITHDRAW,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transaction_time&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;amount&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;comment&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;transferred to &lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;,
        &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;command&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;addTransaction&lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;
            &#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transaction_type&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;TRANSACTION_TYPE_DEPOSIT,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;transaction_time&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;now&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;amount&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;amount&lt;/span&gt;,
            &#39;&lt;span class=&quot;synConstant&quot;&gt;comment&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;=&amp;gt;&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;transferred from &lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;[&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;account_number&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt;,
        &lt;span class=&quot;synSpecial&quot;&gt;])&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; ProcedureStyleTransferMoneyQueryPort
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAndLockAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt;;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; findAccount&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt;;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synType&quot;&gt;interface&lt;/span&gt; ProcedureStyleTransferMoneyCommandPort
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; storeBalance&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;accountNumber&lt;/span&gt;, &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; addTransaction&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void;

    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; notify&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; void;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;h2 id=&quot;実装アイデア&quot;&gt;実装アイデア&lt;/h2&gt;

&lt;h3 id=&quot;レイヤでパッケージを分ける&quot;&gt;レイヤでパッケージを分ける&lt;/h3&gt;

&lt;p&gt;コアレイヤは、それ単独で成り立つものなので、パッケージを分けてしまう方法も考えられる。ここでいうパッケージとは、ビルドや配布ができる単位で、PHP であれば composer パッケージ、Java なら jar ファイルといったものだ。このようにパッケージを分けることで、コアレイヤが依存する外部パッケージを必要最低限なものに制限でき、ビルドサイクルも分離できる。小さなアプリケーションでは考慮する必要は無いが、多くのユースケースを抱える場合やリリースサイクルを分ける場合は一考の余地があるだろう。&lt;/p&gt;

&lt;h3 id=&quot;コアレイヤの範囲&quot;&gt;コアレイヤの範囲&lt;/h3&gt;

&lt;p&gt;前述したとおり、コアレイヤにどこまで含めるのかが本パターンの肝となる。最も範囲を広げるなら、技術詳細を使う一歩手前、分かりやすいところではフレームワークやライブラリの機能を利用する直前までとなる。反対にコアレイヤを狭めていき、ドメインレイヤのみなどにもできるが、そうなるとサービスレイヤにアプリケーションロジックが多数入り込むことになるので、分離という意味では効果が薄くなるかもしれない。&lt;/p&gt;

&lt;h3 id=&quot;ポートの種類&quot;&gt;ポートの種類&lt;/h3&gt;

&lt;p&gt;本エントリでは、ポートをユースケース毎に定義し、その種類を Query、Command、Transaction としている。これには他のアプローチも考えられる。例えば、Command であれば、Account の保存と Transaction の保存、通知を別のポートとして定義することも考えられる。また、ユースケース毎ではなく、複数のユースケースで共通に利用できるように、利用用途でポートを定義する方法もあるだろう。&lt;/p&gt;

&lt;h3 id=&quot;DDD-スタイルへの一歩目&quot;&gt;DDD スタイルへの一歩目&lt;/h3&gt;

&lt;p&gt;すでに見てきたように本パターンは、様々なスタイルでの開発に適用できる。開発初期は、フレームワークの流儀で実装しておく。次に、本パターンを活用してコアレイヤを独立させる。ここまで来れば、コアレイヤはフレームワークから独立しているので、技術詳細から独立したドメインレイヤを作るのは難しくない。このように、ドメインレイヤを作る一歩目として利用する方法も考えられる。&lt;/p&gt;

&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;

&lt;p&gt;Webアプリケーション開発において、オープンソースフレームワークやライブラリの活用は当然のこととなっている。ただ、特にフレームワークに関しては、アプリケーションの基盤として包括的に利用するので密結合になる傾向がある。初期開発においては、フレームワークに密結合で開発することは、動くものを速く作ることができるという悪くない選択だ。しかし、こうしたアプリケーションを運用していくと、フレームワークのバージョンアップの度にアプリケーション全体に手を入れることになり、開発の俊敏性が失われたり、変更コストが大きくなることがある。&lt;/p&gt;

&lt;p&gt;あくまで、アプリケーションが主であり、フレームワークはそれを実現する一手段、ツールに過ぎない。フレームワークを利用しつつも、いかにアプリケーションコアと分離するか、影響を受けないようにするかという観点から、このパターンを考案した。&lt;/p&gt;

&lt;p&gt;開発当初から想像するより、意外とアプリケーションの寿命は長い。初期開発時時に、どこまで先を見越してレイヤ分離を行うかはケースによるだろうが、本パターンであれば、それほど大きな工数をかけずに実現できるのではと見ている。長く運用され、価値を出し続けているアプリケーションに携わっている人のヒントになれば嬉しい。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://alistair.cockburn.us/Hexagonal+architecture&quot;&gt;Alistair.Cockburn.us | Hexagonal architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.tai2.net/hexagonal_architexture.html&quot;&gt;ヘキサゴナルアーキテクチャ(Hexagonal architecture翻訳) | blog.tai2.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html&quot;&gt;The Clean Architecture | 8th Light&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.tai2.net/the_clean_architecture.html&quot;&gt;クリーンアーキテクチャ(The Clean Architecture翻訳) | blog.tai2.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jeffreypalermo.com/blog/the-onion-architecture-part-1/&quot;&gt;The Onion Architecture : part 1 : Jeffrey Palermo (.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jeffreypalermo.com/blog/onion-architecture-part-4-after-four-years/&quot;&gt;Onion Architecture: Part 4 - After Four Years : Jeffrey Palermo (.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-68044b2a&quot; name=&quot;f-68044b2a&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;&lt;a href=&quot;http://php.net/manual/ja/book.ds.php&quot;&gt;http://php.net/manual/ja/book.ds.php&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20180510/20180510235928.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>懇親会ではなく本編で交流できた PHPerKaigi 2018</title>
        <link href="https://blog.shin1x1.com/entry/phperkaigi2018"/>
        <id>hatenablog://entry/17391345971628348828</id>
        <published>2018-03-22T21:49:02+09:00</published>
        <updated>2018-05-22T11:03:44+09:00</updated>        <summary type="html">phperkaigi.jp PHPerKaigi 2018 に参加してきました。 「PHP の現場」公開収録 主催の長谷川さんからお誘いを頂き、 @goodoo さんをゲストにお招きして、「PHP の現場」の公開収録をやってきました。 「初の公開収録」と言いたいところなのですが、1 回目が勉強会で話している模様の録音だったので、それを入れると 2 回目ですね。公開収録と銘打って収録したのが初です。 Twitter で公開収録を呼びかけたところ、立ち見の方が出るほど多くの方に集まっていただきました！ありがとうございましたm(_ _)m せっかく集まって頂いたのですが、私と @goodoo さんと…</summary>
        <content type="html">&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fphperkaigi.jp%2F2018%2F&quot; title=&quot;PHPerKaigi 2018&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://phperkaigi.jp/2018/&quot;&gt;phperkaigi.jp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;PHPerKaigi 2018 に参加してきました。&lt;/p&gt;

&lt;h2&gt;「PHP の現場」公開収録&lt;/h2&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;a href=&quot;http://f.hatena.ne.jp/shin1x1/20180323121324&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;url&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20180323/20180323121324.jpg&quot; alt=&quot;f:id:shin1x1:20180323121324j:image:w600&quot; title=&quot;f:id:shin1x1:20180323121324j:image:w600&quot; class=&quot;hatena-fotolife&quot; style=&quot;width:600px&quot; itemprop=&quot;image&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;主催の長谷川さんからお誘いを頂き、 &lt;a href=&quot;https://twitter.com/goodoo&quot;&gt;@goodoo&lt;/a&gt; さんをゲストにお招きして、「PHP の現場」の公開収録をやってきました。&lt;/p&gt;

&lt;p&gt;「初の公開収録」と言いたいところなのですが、1 回目が勉強会で話している模様の録音だったので、それを入れると 2 回目ですね。公開収録と銘打って収録したのが初です。&lt;/p&gt;

&lt;p&gt;Twitter で公開収録を呼びかけたところ、立ち見の方が出るほど多くの方に集まっていただきました！ありがとうございましたm(_ _)m&lt;/p&gt;

&lt;p&gt;せっかく集まって頂いたのですが、私と @goodoo さんとで向き合って話すスタイルだったのと賑やかな会場だったこともあり、音声が聞きづらい状況になってしまいました。。。&lt;/p&gt;

&lt;p&gt;正直、音源に周りの音がのることは予想していたのですが、収録現場で話している声が聞き取りづらくなるのは考えていませんでした。会場が予想よりもかなり盛り上がっていたということは、イベントとして大成功なので、これは全く問題ありません。&lt;/p&gt;

&lt;p&gt;公開収録に望む環境作りの話なので、今回のことは貴重な経験として、次回があれば生かしていきたいです。&lt;/p&gt;

&lt;p&gt;当日の収録分はすでに公開していますので、こちらでお楽しみ下さい。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fphp-genba.shin1x1.com%2F18&quot; title=&quot;18. PHP を選ぶ / サイバーエージェントの現場（goodoo）  | PHPの現場&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://php-genba.shin1x1.com/18&quot;&gt;php-genba.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftogetter.com%2Fli%2F1207100&quot; title=&quot;全PHPerのためのポッドキャスト「PHPの現場」公開収録 in #PHPerKaigi ゲストは @goodoo さん#phpgenba&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://togetter.com/li/1207100&quot;&gt;togetter.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;懇親会の盛り上がりを本編で&lt;/h2&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;a href=&quot;http://f.hatena.ne.jp/shin1x1/20180323121312&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;url&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20180323/20180323121312.jpg&quot; alt=&quot;f:id:shin1x1:20180323121312j:image:w600&quot; title=&quot;f:id:shin1x1:20180323121312j:image:w600&quot; class=&quot;hatena-fotolife&quot; style=&quot;width:600px&quot; itemprop=&quot;image&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;イベントに目を向けると、Track A は素晴らしいセッションが目白押しでした。そして、Track B の盛り上がりが印象的でした。&lt;/p&gt;

&lt;p&gt;上記でも少し触れましたが、Track B には丸テーブルとそれを囲む椅子、ホワイトボードが複数用意されており、テーマを決めて、ディスカッションができるようになっていました。&lt;/p&gt;

&lt;p&gt;それ以外にもテーブルや椅子は用意されており、自由に話したり、ハックしたりできるようになっています。また、コーヒーやドーナッツといった軽食も用意されていて、Track A を聞いて一息付きたい時に自然と足を運ぶようになっていたのも良かったです。&lt;/p&gt;

&lt;p&gt;トークセッションを聞き続けるのは意外に疲れるので補給できるスペースはありがたいです。以前参加した Microsoft の Build でも飲食が充実していて、廊下には常にドリンクや軽食があったので、セッションが終わるごとに何か食べていた気がします ;)&lt;/p&gt;

&lt;p&gt;この盛り上がりは、自分が運営するイベントで課題として考えていた「懇親会が本番」問題の一つの解を見た気がします。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.1x1.jp%2Fblog%2F2012%2F12%2Fwhy_i_hold_a_study_meeting.html&quot; title=&quot;勉強会なんてやらなくても良い&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://www.1x1.jp/blog/2012/12/why_i_hold_a_study_meeting.html&quot;&gt;www.1x1.jp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;やはり、色々な人と技術の話をして楽しむというのは、同じ場所に人が集まるカンファレンスならではのものです。もちろん、これは懇親会でできることなのですが、どうにかそれを本編でできないかというのが頭の中にありました。また、アルコールがそれほど得意ではない人間からすると、飲まずに交流できる場があるというのもいいなとも思います。飲みの席は好きなのですが、盛り上がりすぎると腰を据えてじっくりと話すというより、ノリ優先で盛り上がる方向に行くので、そうでは無い場も欲しかったりします。（ノリ優先で盛り上がるのも好きです！）&lt;/p&gt;

&lt;p&gt;ただ、交流となると、知り合いがいれば良いですが、見ず知らずの人と話すのは難しいものです。それを解決する方法として「Ask The Speaker」や相談会といった企画が上手く機能していたように見えました。私も「Laravel 相談会」に少し参加したのですが、参加している方々から色々な質問が出てきて、それについてみんなで話すというのが自然に行われていました。話題が提供されているというのはすごく良くて、お互いを知らない者同士でも話しやすいです。これは素晴らしい取り組みでした。&lt;/p&gt;

&lt;p&gt;「アンカンファレンス」じゃなくて「相談会」と名付けたもの、何をする場なのかが分かりやすくて、うまいなあと。&lt;/p&gt;

&lt;p&gt;これだけ盛り上がった Track B なので、来年はもっと広い会場でゆったりできるようになると嬉しいです！&lt;/p&gt;

&lt;h2&gt;次のイベントへの橋渡し&lt;/h2&gt;

&lt;p&gt;長谷川さんの最後の挨拶で印象的だったのが、福岡や関西といった他の地方のカンファレンスへの誘導を積極的に行っていたことでした。&lt;/p&gt;

&lt;p&gt;実際、TL などを見ていると多くの人が、福岡や関西に行きたい！と書いていて、トークにも応募したようです。福岡では、前年よりトーク応募数が増えたようですけど、これは PHPerKaigi での煽動（？）が一部効いているのではないでしょうか。&lt;/p&gt;

&lt;p&gt;こうして主催者が、次のイベントへの橋渡しをするというのも、これまであまり無かったので、全てのイベントが一つのコミュニティにあるというのがあらためて感じられて良かったです。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;公開収録や Track A のセッション、Track B の交流などなど盛りだくさんで楽しいイベントでした。&lt;/p&gt;

&lt;p&gt;素晴らしいイベントをありがとうございました。来年も楽しみにしています！&lt;/p&gt;

&lt;h2&gt;PHPerKaigi の現場&lt;/h2&gt;

&lt;p&gt;というような話を PHP の現場で収録します。&lt;/p&gt;

&lt;p&gt;ゲストは、もちろん &lt;a href=&quot;https://twitter.com/tomzoh&quot;&gt;@tomzoh&lt;/a&gt; さんと &lt;a href=&quot;https://twitter.com/cakephper&quot;&gt;@cakephper&lt;/a&gt; さんです。お楽しみに :)&lt;/p&gt;

&lt;p&gt;【追記】公開しました。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fphp-genba.shin1x1.com%2F19&quot; title=&quot;19. PHPerKaigi の現場（tomzoh / cakephper）  | PHPの現場&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://php-genba.shin1x1.com/19&quot;&gt;php-genba.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20180323/20180323121324.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>PhpStorm + Docker for Mac（docker-compose）での PHPUnit と Remote Debug の設定</title>
        <link href="https://blog.shin1x1.com/entry/setup-test-and-debug-on-phpstorm-and-docker-for-mac"/>
        <id>hatenablog://entry/8599973812311422404</id>
        <published>2017-10-27T07:00:00+09:00</published>
        <updated>2017-10-27T11:44:10+09:00</updated>        <summary type="html">PhpStorm から Docker for Mac の Docker コンテナ内で PhpUnit のテスト実行や Remote Debug を行うための設定です。 下記バージョンにて構築を行いました。 PhpStorm 2017.2.4 Docker for Mac 17.09.0-ce-mac35 PHP 7.1（ php:7.1-apache ベース ） Xdebug 2.5 サンプルプロジェクトの取得 サンプルプロジェクトを用意したので、このプロジェクトをベースに設定を行います。このプロジェクトには、初期状態の Laravel アプリケーション（5.5）が含まれています。 githu…</summary>
        <content type="html">&lt;p&gt;PhpStorm から Docker for Mac の Docker コンテナ内で PhpUnit のテスト実行や Remote Debug を行うための設定です。&lt;/p&gt;

&lt;p&gt;下記バージョンにて構築を行いました。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PhpStorm 2017.2.4&lt;/li&gt;
&lt;li&gt;Docker for Mac 17.09.0-ce-mac35&lt;/li&gt;
&lt;li&gt;PHP 7.1（ &lt;code&gt;php:7.1-apache&lt;/code&gt; ベース ）&lt;/li&gt;
&lt;li&gt;Xdebug 2.5&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;サンプルプロジェクトの取得&lt;/h2&gt;

&lt;p&gt;サンプルプロジェクトを用意したので、このプロジェクトをベースに設定を行います。このプロジェクトには、初期状態の Laravel アプリケーション（5.5）が含まれています。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Fphpstorm-with-docker-for-mac-sample&quot; title=&quot;shin1x1/phpstorm-with-docker-for-mac-sample&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/phpstorm-with-docker-for-mac-sample&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;git で clone して、make を実行すると &lt;code&gt;composer install&lt;/code&gt; と &lt;code&gt;docker-compose up&lt;/code&gt; が実行されます。ブラウザで、 &lt;a href=&quot;http://localhost:8000/&quot;&gt;http://localhost:8000/&lt;/a&gt; にアクセスすると、Laravel の初期画面が表示されます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;$ git clone https://github.com/shin1x1/phpstorm-with-docker-for-mac-sample
$ cd phpstorm-with-docker-for-mac-sample

$ make
もしくは
$ docker-compose run composer install
$ docker-compose up -d&lt;/pre&gt;


&lt;p&gt;docker-compose.yml は下記のようになっています。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php-cli&lt;/code&gt; は PHPUnit を実行する、&lt;code&gt;web&lt;/code&gt; は Web アプリケーションを実行するサービスです。現状、&lt;code&gt;web&lt;/code&gt; サービスを起動中に PhpStorm から PHPUnit を実行すると、先に起動していた &lt;code&gt;web&lt;/code&gt; コンテナが落ちるので、それを回避するためです。&lt;/p&gt;

&lt;p&gt;どちらも image や volumes などの設定は同じです。web はポートマッピングを指定しています。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;version: &amp;#39;3&amp;#39;
services:
  php-cli:
    build: ./docker/web
    volumes:
      - ./:/var/www/html
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
  web:
    build: ./docker/web
    volumes:
      - ./:/var/www/html
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
    ports:
      - &amp;#34;8000:80&amp;#34;
  composer:
    image: composer
    volumes:
      - ./:/app
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini
    working_dir: /app&lt;/pre&gt;


&lt;p&gt;PhpStorm でリポジトリディレクトリをプロジェクトとして開きます。&lt;/p&gt;

&lt;h2&gt;1. CLI Interpreter&lt;/h2&gt;

&lt;p&gt;まず、PHP CLI Interpreter に docker-compose のサービスを指定します。&lt;/p&gt;

&lt;p&gt;[Preferences] - [Languages &amp;amp; Frameworks] - [PHP] を開き、CLI Interpreter 右端にある「...」をクリックします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026110503.png&quot; alt=&quot;f:id:shin1x1:20171026110503p:plain&quot; title=&quot;f:id:shin1x1:20171026110503p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;CLI Interpreters が開くので、左上にある「+」をクリックして Interpreter を選択します。ここでは、一番上にある「From Docker, Vagrant, VM, Remote...」を選択します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026111032.png&quot; alt=&quot;f:id:shin1x1:20171026111032p:plain&quot; title=&quot;f:id:shin1x1:20171026111032p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Configure Remote PHP Interpreter が開くので、右端の「Docker Compose」を選択します。Server の横にある「New...」クリックします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026144726.png&quot; alt=&quot;f:id:shin1x1:20171026144726p:plain&quot; title=&quot;f:id:shin1x1:20171026144726p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Docker が開くので、「Docker for Mac」を選択して、OK をクリックします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026111848.png&quot; alt=&quot;f:id:shin1x1:20171026111848p:plain&quot; title=&quot;f:id:shin1x1:20171026111848p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Server で作成した「Docker」選択さています。Service で、実行する PHP インタプリタ（php コマンド）が含まれるサービスを選択します。ここでは、&lt;code&gt;php-cli&lt;/code&gt; を指定しています。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026144902.png&quot; alt=&quot;f:id:shin1x1:20171026144902p:plain&quot; title=&quot;f:id:shin1x1:20171026144902p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;OK をクリックすると、docker-compose run が実行され、自動でコンテナ内の情報を取得します。CLI Interpreters に、取得した PHP や Xdebug のバージョンが表示されます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026144537.png&quot; alt=&quot;f:id:shin1x1:20171026144537p:plain&quot; title=&quot;f:id:shin1x1:20171026144537p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;OK をクリックすると、CLI Interpreter に作成したインタプリタが表示されます。Path mappings にも docker-compose.yml の volumes で指定している情報が設定されています。OK をクリックして完了です。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026113143.png&quot; alt=&quot;f:id:shin1x1:20171026113143p:plain&quot; title=&quot;f:id:shin1x1:20171026113143p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;2. PHPUnit&lt;/h2&gt;

&lt;p&gt;PHPUnit を実行できるように設定します。&lt;/p&gt;

&lt;p&gt;[Preferences] - [Languages &amp;amp; Frameworks] - [PHP] - [Test Frameworks] を開きます。「+」をクリックして、[Select Configuration Types] から [PHPUnit By Remote Interpreter] を選択します。すると、下記の [PHPUnit By Remote Interpreter] が開きます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026114858.png&quot; alt=&quot;f:id:shin1x1:20171026114858p:plain&quot; title=&quot;f:id:shin1x1:20171026114858p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Interpreter に先程作成したインタープリタを選択します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026115047.png&quot; alt=&quot;f:id:shin1x1:20171026115047p:plain&quot; title=&quot;f:id:shin1x1:20171026115047p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;OK をクリックすると、下記のように CLI Interpreter や Path mappings が設定されます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026115255.png&quot; alt=&quot;f:id:shin1x1:20171026115255p:plain&quot; title=&quot;f:id:shin1x1:20171026115255p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;PHPUnit が認識できていないので、[Use Composer autoloader] を選択し、[Path to script] に autoload.php のパスを入力します。このパスはコンテナ内のパスなので注意して下さい。ここでは、&lt;code&gt;/var/www/html/vendor/autoload.php&lt;/code&gt; を入力しています。入力して、横の更新をクリックすると PHPUnit バージョンが表示されます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026115630.png&quot; alt=&quot;f:id:shin1x1:20171026115630p:plain&quot; title=&quot;f:id:shin1x1:20171026115630p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;ついでに、phpunit.xml のパスも設定しておきます。[Default configuration file] の左横にチェックを付けて、 &lt;code&gt;/var/www/html/phpunit.xml&lt;/code&gt;
 を入力します。最後に OK をクリックします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026115836.png&quot; alt=&quot;f:id:shin1x1:20171026115836p:plain&quot; title=&quot;f:id:shin1x1:20171026115836p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;次に、[Edit Configurations]（Run/Debug Configurations） を開いて、左端の「+」から「PHPUnit」を選択します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026120203.png&quot; alt=&quot;f:id:shin1x1:20171026120203p:plain&quot; title=&quot;f:id:shin1x1:20171026120203p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Name を任意のものに変更して、[Test Runner] で 「Defined in the configuration file」を選択します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026121125.png&quot; alt=&quot;f:id:shin1x1:20171026121125p:plain&quot; title=&quot;f:id:shin1x1:20171026121125p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;OK をクリックして設定完了です。&lt;/p&gt;

&lt;p&gt;PhpStorm ウィンドウ上部にある下記の右矢印や CTRL + R など押すと、PHPUnit を実行できます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026121206.png&quot; alt=&quot;f:id:shin1x1:20171026121206p:plain&quot; title=&quot;f:id:shin1x1:20171026121206p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026120850.png&quot; alt=&quot;f:id:shin1x1:20171026120850p:plain&quot; title=&quot;f:id:shin1x1:20171026120850p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;3. Remote Debug&lt;/h2&gt;

&lt;p&gt;Remote Debug の設定を行います。Remote Debug を設定するには、Xdebug の設定、PhpStorm の設定が必要になります。&lt;/p&gt;

&lt;h3&gt;3-1. Xdebug の設定&lt;/h3&gt;

&lt;p&gt;php.ini などで、Xdebug を下記のように設定します。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;; xdebug
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_connect_back = Off
xdebug.remote_host = docker.for.mac.localhost&lt;/pre&gt;


&lt;p&gt;ポイントは、xdebug.remote_host です。以前は、Docker for Mac の問題でコンテナからホストの
 通信が上手く動作しない場合があったのですが、 &lt;code&gt;docker.for.mac.localhost&lt;/code&gt; を指定することで正常に通信できるようになりました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds&quot;&gt;https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;サンプルプロジェクトでは、docker/web/php.ini にて設定済です。&lt;/p&gt;

&lt;h3&gt;3-2. PhpStorm の設定&lt;/h3&gt;

&lt;p&gt;[Edit Configurations]（Run/Debug Configurations）を開いて、左上の「+」をクリックして、[PHP Remote Debug] を選択します。新しい設定が開くので、[Servers] 横にある「...」をクリックします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026145438.png&quot; alt=&quot;f:id:shin1x1:20171026145438p:plain&quot; title=&quot;f:id:shin1x1:20171026145438p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;[Servers] が開きくので、左上にある「+」をクリックして、追加します。&lt;/p&gt;

&lt;p&gt;[Name] に &lt;code&gt;Docker&lt;/code&gt; 、[Host]  に &lt;code&gt;localhost&lt;/code&gt;、[Port] に &lt;code&gt;8000&lt;/code&gt; を入力します。つづけて、[Use path mappings] にチェックを入れて、プロジェクトルートディレクトリのマッピング先に &lt;code&gt;/var/www/html&lt;/code&gt; を設定します。これにより、ホスト側のディレクトリとコンテナ側のディレクトリをマッピングします。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026131205.png&quot; alt=&quot;f:id:shin1x1:20171026131205p:plain&quot; title=&quot;f:id:shin1x1:20171026131205p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;OK をクリックすると、[Servers] に &lt;code&gt;Docker&lt;/code&gt; が指定されています。[Name] に任意の名前、[ide key] にダミーのキーを入力します。なお、[ide key] は、Xdebug の設定で &lt;code&gt;xdebug.idekey&lt;/code&gt; を指定している場合はその値を指定します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026145639.png&quot; alt=&quot;f:id:shin1x1:20171026145639p:plain&quot; title=&quot;f:id:shin1x1:20171026145639p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;これで設定は完了です。&lt;/p&gt;

&lt;h3&gt;3-3. Remote Debug - Web&lt;/h3&gt;

&lt;p&gt;Web アプリケーションをステップ実行してみましょう。&lt;/p&gt;

&lt;p&gt;[Run] メニューにある [Start Listening for PHP Debug Connections] をクリックして ON にします。これで、Xdebug からの通信を受け付ける状態になります。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026153906.png&quot; alt=&quot;f:id:shin1x1:20171026153906p:plain&quot; title=&quot;f:id:shin1x1:20171026153906p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;PhpStorm ウィンドウ右上にある受話器アイコンをクリックしても ON にできます。（下記は、OFF の状態）&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026153947.png&quot; alt=&quot;f:id:shin1x1:20171026153947p:plain&quot; title=&quot;f:id:shin1x1:20171026153947p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;動作確認のために、[Run] メニューにある [Break at first line in PHP Scripts] もクリックして ON にしておきます。これを ON にすると、実行された最初の行で処理が停止し、デバッガが起動します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026154318.png&quot; alt=&quot;f:id:shin1x1:20171026154318p:plain&quot; title=&quot;f:id:shin1x1:20171026154318p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;この状態で、ブラウザで &lt;a href=&quot;http://localhost:8000/&quot;&gt;http://localhost:8000/&lt;/a&gt; を開くとデバッガが起動します。ステップ実行すると順に処理を実行できます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026154456.png&quot; alt=&quot;f:id:shin1x1:20171026154456p:plain&quot; title=&quot;f:id:shin1x1:20171026154456p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;次は、ブレイクポイントを設定して、処理が停止するか確認します。先ほどの [Break at first line in PHP Scripts] を OFF にしておきます。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;routes/web.php&lt;/code&gt; の下記の行にブレイクポイントを設定します。ブレイクポイントは、下記の矢印の先あたりをクリックすると設定できます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026154747.png&quot; alt=&quot;f:id:shin1x1:20171026154747p:plain&quot; title=&quot;f:id:shin1x1:20171026154747p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;この状態で、再度ブラウザを再読込すると該当行で処理が停止します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026154907.png&quot; alt=&quot;f:id:shin1x1:20171026154907p:plain&quot; title=&quot;f:id:shin1x1:20171026154907p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;PhpStorm のデバッガは優秀で、その時点での変数やスタックトレースなど多くの情報を表示できます。また、条件付きブレイクポイントを設定したり、中断した時点でのコンテキストでコードを実行したりもできます。&lt;/p&gt;

&lt;h3&gt;3-3. Remote Debug - CLI(PHPUnit)&lt;/h3&gt;

&lt;p&gt;PhpStorm から PHP スクリプトを実行した場合もデバッガが使えます。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;artisan&lt;/code&gt; を開いて、下記のようにブレイクポイントを設定しておきます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026160529.png&quot; alt=&quot;f:id:shin1x1:20171026160529p:plain&quot; title=&quot;f:id:shin1x1:20171026160529p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;この状態で、右クリックして [Debug &#39;xxxx&#39;] （CTRL + SHIFT + D）をクリックすると、docker-compose 経由で arisan が実行されます。実行がはじまるとすぐにブレイクポイントを設定した箇所で処理が停止し、デバッガが起動します。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026184253.png&quot; alt=&quot;f:id:shin1x1:20171026184253p:plain&quot; title=&quot;f:id:shin1x1:20171026184253p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;PHPUnit でテストを実行する場合も同じ方法でデバッガが使えます。テストケースでブレイクポイントを設定後に、Debug 実行すると、テストが起動して、ブレイクポイントの箇所で処理が停止できます。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026184842.png&quot; alt=&quot;f:id:shin1x1:20171026184842p:plain&quot; title=&quot;f:id:shin1x1:20171026184842p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;PhpStorm の Docker サポートは前からあったのですが、docker-compose に対応してなかったり、Docker for Mac 側の問題があったりなどがあり、Vagrant との連携に比べると今ひとつの印象がありました。&lt;/p&gt;

&lt;p&gt;改善が進み、それらが解消してきたので、PhpStorm 上からも快適に利用できるようになりました。やはり、IDE から操作できるのは便利ですね。&lt;/p&gt;

&lt;h2&gt;[参考] Vagrant 連携設定&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.1x1.jp/blog/2014/08/how-to-setup-php-remote-debug-with-vagrant-vm.html&quot;&gt;PhpStorm &amp;#x304B;&amp;#x3089; Vagrant VM &amp;#x306E; PHP &amp;#x30A2;&amp;#x30D7;&amp;#x30EA;&amp;#x30B1;&amp;#x30FC;&amp;#x30B7;&amp;#x30E7;&amp;#x30F3;&amp;#x3092;&amp;#x30EA;&amp;#x30E2;&amp;#x30FC;&amp;#x30C8;&amp;#x30C7;&amp;#x30D0;&amp;#x30C3;&amp;#x30B0;&amp;#x3059;&amp;#x308B;&amp;#xFF08;Web &amp;amp; CLI&amp;#xFF09; - Shin x blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.1x1.jp/blog/2014/09/phpstorm-phpunit-by-remote-innterpreter-with-vagrant.html&quot;&gt;PhpStorm 8 &amp;#x3067;&amp;#x3001;Vagrant VM &amp;#x306E; PHPUnit &amp;#x3092; IDE &amp;#x304B;&amp;#x3089;&amp;#x5B9F;&amp;#x884C;&amp;#x3059;&amp;#x308B; - Shin x blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171026/20171026120850.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>TDD を体験する本「テスト駆動開発」</title>
        <link href="https://blog.shin1x1.com/entry/tdd-by-example"/>
        <id>hatenablog://entry/8599973812310704928</id>
        <published>2017-10-25T00:00:16+09:00</published>
        <updated>2017-10-25T00:32:46+09:00</updated>        <summary type="html">TDD の原典である テスト駆動開発 が、t_wada さんによって再翻訳されるということで興味を持っていたところ、見本誌を送って頂きました。ありがとうございました。早速、拝読させて頂きました。 以前の翻訳版である「テスト駆動開発入門」が出版された頃、XP 本や他の xUnit 本などは読んだのですが、本書は未読でした。テストをコードで書くことや自動化には興味があったので、おそらく手に取ったことはあったと思うのですが、購入には至りませんでした。 当時は、新しい開発プロセスの一つとしてテストの自動化を捉えていましたが、それを日々の開発で当たり前に行っている中で本書に触れてみてどのように感じるかと…</summary>
        <content type="html">&lt;p&gt;TDD の原典である &lt;a href=&quot;http://d.hatena.ne.jp/asin/4274217884/shinxblog-22&quot;&gt;テスト駆動開発&lt;/a&gt; が、t_wada さんによって再翻訳されるということで興味を持っていたところ、見本誌を送って頂きました。ありがとうございました。早速、拝読させて頂きました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4274217884/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51hsd-b1RTL.jpg&quot; alt=&quot;テスト駆動開発&quot; title=&quot;テスト駆動開発&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以前の翻訳版である「テスト駆動開発入門」が出版された頃、XP 本や他の xUnit 本などは読んだのですが、本書は未読でした。テストをコードで書くことや自動化には興味があったので、おそらく手に取ったことはあったと思うのですが、購入には至りませんでした。&lt;/p&gt;

&lt;p&gt;当時は、新しい開発プロセスの一つとしてテストの自動化を捉えていましたが、それを日々の開発で当たり前に行っている中で本書に触れてみてどのように感じるかという視点で読み進めました。&lt;/p&gt;

&lt;p&gt;本書は、三部構成となっています。第 1 部 と 第 2 部 では、TDD を実践しながらコードを書き上げていく過程を解説しています。第 3 部は、テストに関する様々な話題がまとめらており、コラム集のようになっています。三部構成とはいったものの、今回加筆された付録 C も内容を考えると一つのパートとも言えるので、実質は四部構成といって良いかもしれません。&lt;/p&gt;

&lt;h2&gt;TDD でコードが実装されていく楽しさ&lt;/h2&gt;

&lt;p&gt;第 1 部では、通貨の演算を TDD で実装していきます。本文とコードが分かりやすいように記されているので、まるでライブコーディングを見ているような気分で読んでいきました。&lt;/p&gt;

&lt;p&gt;Dollar の演算を実装し、Franc が追加され、相互に変換が加わりといった具合にコードが進化していく様は、まるで物語を読んでいるようで、先の展開が気になり、どんどんページをめくっていきました。最終的に、え、その形で終わり？と思ったところで、TDD を学ぶための例題だったことを思い出したくらいです。&lt;/p&gt;

&lt;p&gt;コーディングを見ていく過程で自然と TDD のプラクティスを知ることができるというのは良くできた構成だと感じました。&lt;/p&gt;

&lt;h2&gt;xUnit の実装&lt;/h2&gt;

&lt;p&gt;第 2 部では、シンプルなテスティングフレームワークを TDD で実装していきます。日頃利用しているテスティングフレームワークのコア部分を実装するということで、題材としても想像しやすいですし、どのように実現するのか関心を持ちました。&lt;/p&gt;

&lt;p&gt;setUp() と tearDown() を Template Method パターンで実装したり、テストスウィートで複数のテストケースをまとめて実行したりと、おなじみの機能の実装が体験できるのはいいですね。&lt;/p&gt;

&lt;h2&gt;2017 年のテスト駆動開発&lt;/h2&gt;

&lt;p&gt;今回の再出版に際して、t_wada さんが加筆されたのが、付録 C です。本書が初回出版された以後の TDD 界隈の流れ、そして現在の状況が解説されています。TDD / BDD などの流れが丁寧に記されていて、現在の状況にどのように繋がっているかが分かります。（もちろん、数年前にあった DHH の「TDD is dead. Long live testing.」にも触れられています）&lt;/p&gt;

&lt;p&gt;特に、教条主義に触れた箇所はいいですね。TDD の話題では、教条主義的な立場から、これこそが TDD、それ以外は異端といったような意見が見受けられますが、まさにその中心となる本書で、教条主義化について触れられています。&lt;/p&gt;

&lt;p&gt;この箇所では、教条主義的に本書や本道とされることのみを良しとするわけでもなく、また、反対に教条主義をただ批判するわけでもなく、現状を把握した上で一歩引いた視点で解説されており、バランスの取れた内容となっているのが、さすがでした。&lt;/p&gt;

&lt;p&gt;この章は書き下ろしということで、文体含めて特に読みやすかったです。いつか、t_wada さんが、テストに関する書籍（より実践的な内容を含んで）を執筆されることを期待せずにはいられません。&lt;/p&gt;

&lt;h2&gt;写経&lt;/h2&gt;

&lt;p&gt;通読の後、Part1 と Part2 について写経を行いました。TDD は、当然ながら言語に依存するようなものではないので、PHP で実装してみました。リポジトリには、Makefile があるので、&lt;code&gt;make&lt;/code&gt; コマンドで、依存解決とテスト実行ができます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Fbook-tdd-php&quot; title=&quot;shin1x1/book-tdd-php&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/book-tdd-php&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;本書にもあるとおり、写経で実際にコードを書いてみると、また異なる印象を受けますね。特に、Part1 では、かなり歩幅を落として、小刻みにサイクルを回していくので、日頃の開発でテストを書くのとは異なるリズムで進めることになりました。これほど小さな歩幅でサイクルを回して進めることは無いので、これは面白い体験でした。&lt;/p&gt;

&lt;p&gt;このスピード感の違いや違和感（自分のリズムとは違うという意味で）は、実際にコードを書いてみて、より感じる部分なので、本書を手にした方はぜひコードを書いて体験してみてください。&lt;/p&gt;

&lt;p&gt;実は、見本誌（紙）とは別に電子版（PDF）を購入して、併用で読み進めたのですが、写経の時は紙の方が見開きで見ることができるのでやりやすかったです。&lt;/p&gt;

&lt;h2&gt;TDD はテスト技法では無い&lt;/h2&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;皮肉なことに、TDD はテスト技法ではない。TDD は分析技法であり、設計技法であり、実際には開発のすべてのアクティビティを構造化する技法なのだ。

「テスト駆動開発」&lt;/pre&gt;


&lt;p&gt;本書を読む上で、また TDD を知る上で重要なのは、これですね。もちろん書籍内では、テストの書き方についても解説されていますが、テスティングフレームワークの機能を活用して、いかにテストを効率的に書くかという視点の書籍では無いと感じました。（ただ、実際のところ、テスト技法を知る本としての役割も果たしています。）&lt;/p&gt;

&lt;p&gt;つまり、TDD を行うかどうかという話とテストを書くか書かないかは別ということです。&lt;/p&gt;

&lt;p&gt;原著が登場した頃は、まだテストを書く文化がそれほど一般的ではなかったと思うので、このあたりが混同されがち（自分を含めて）だったと思うのですが、テストを書くということが一般化した今なら、テストで開発を推し進める TDD という開発技法がより正しく伝わるのではないでしょうか。&lt;/p&gt;

&lt;p&gt;そういった意味では、2017 年の今に本書が登場したというのは何か意味があるようにも感じます。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;本書で感じた TDD の小気味の良いリズム感は気持ち良いものでした。日々の開発を TDD で行うかは別にしても、思ったように開発が進まない時や実装が難しいような場合に使える手法として持っておくと良いですね。&lt;/p&gt;

&lt;p&gt;ときおり、開発を始める前の肩慣らしのように TDD で軽くコードを書いてみるというのも良さそうです。&lt;/p&gt;

&lt;p&gt;テストを書いている人はもちろんのこと、テストを書いていなくとも開発手法に興味がある人は、ぜひ本書で TDD を体験してみてください。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51hsd-b1RTL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;テスト駆動開発&quot; title=&quot;テスト駆動開発&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/shinxblog-22/&quot;&gt;テスト駆動開発&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; Kent Beck,和田卓人&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; オーム社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2017/10/14&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本（ソフトカバー）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4274217884/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (1件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
</content>        
        <category term="book" label="book" />
        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/51hsd-b1RTL.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「型を意識した PHP アプリケーション」を発表しました / PHP カンファレンス 2017</title>
        <link href="https://blog.shin1x1.com/entry/php-development-with-type-declration"/>
        <id>hatenablog://entry/8599973812306772118</id>
        <published>2017-10-11T06:00:00+09:00</published>
        <updated>2017-10-11T06:00:22+09:00</updated>        <summary type="html">PHP カンファレンス 2017 にて「型を意識した PHP アプリケーション」を発表してきました。 発表 github.com 型宣言が付いていないコードに、スカラー型の型宣言、ドメインに特化した型の型宣言と型（クラス）の実装を行うライブコーディングをメインにしました。 ライブコーディングの部分は動画を見た方が分かりやすいです。下記の 13:00 頃からコーディングがはじめるので、こちらも合わせてどうぞ。 きっちりまとめられた資料をベースに発表するのも良いのですが、実際にコードを書いていくのを見た方がイメージも伝わりやすいですし、私自身も人がコードを書く様を見るのが好きなので、この構成にしま…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://phpcon.php.gr.jp/2017/&quot;&gt;PHP カンファレンス 2017&lt;/a&gt; にて「型を意識した PHP アプリケーション」を発表してきました。&lt;/p&gt;

&lt;h2&gt;発表&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;519f477853644c7999b5a447d9ebda25&quot; data-ratio=&quot;1.33333333333333&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fshin1x1%2Fphpcon2017&quot; title=&quot;shin1x1/phpcon2017&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://github.com/shin1x1/phpcon2017&quot;&gt;github.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;型宣言が付いていないコードに、スカラー型の型宣言、ドメインに特化した型の型宣言と型（クラス）の実装を行うライブコーディングをメインにしました。&lt;/p&gt;

&lt;p&gt;ライブコーディングの部分は動画を見た方が分かりやすいです。下記の 13:00 頃からコーディングがはじめるので、こちらも合わせてどうぞ。&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/Ttwye1aYMV0&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;


&lt;p&gt;きっちりまとめられた資料をベースに発表するのも良いのですが、実際にコードを書いていくのを見た方がイメージも伝わりやすいですし、私自身も人がコードを書く様を見るのが好きなので、この構成にしました。&lt;/p&gt;

&lt;p&gt;また、質疑応答の時間を長めに取りたかったので、本編は少し短めにして時間が余るように調整しました。当初の内容からどんどん引き算していって、内容をシンプルにしたので、結果的には進めやすかったですし、何より質疑応答で多くの質問を頂けたので良かったです。&lt;/p&gt;

&lt;p&gt;やっぱり、せっかくのライブなので、一方通行よりキャッチボールできるのが楽しいです。メインの発表中もやりとりしながら進められる形ができるといいですね。&lt;/p&gt;

&lt;p&gt;Twitter での反応では概ね好評だったようなので発表して良かったです。tweet ありがとうございました！&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftogetter.com%2Fli%2F1159139&quot; title=&quot;型を意識した PHP アプリケーション開発&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://togetter.com/li/1159139&quot;&gt;togetter.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;「PHP の現場」&lt;/h2&gt;

&lt;p&gt;ここ最近の発表では自己紹介で「PHP の現場」を紹介して、聴いたことがあるかを聞いています。PHP カンファレンス関西、builderscon 、PHP カンファレンスと続いた中では今回が一番多くの方に手を上げて頂きました。&lt;/p&gt;

&lt;p&gt;発表後も会場で、何名かの方に「聴いてます！」と声をかけて頂きました。&lt;/p&gt;

&lt;p&gt;特に印象的だったのは、聴いたエピソードについて話を振ってくれる方が多かったということです。エピソードの内容は、基本私が聞きたいことなので、自然と会話が盛り上がりました。自分が面白いと思ったことを共感してもらえ、さらにそれをネタに会話が広がるというのは本当に嬉しいことです。（もちろん、ただ聴いてますと言って頂けるだけでも嬉しいです！）&lt;/p&gt;

&lt;p&gt;あらためて、聴いて頂きありがとうございますm(_ _)m&lt;/p&gt;

&lt;p&gt;今後もイベントなどで会う機会があれば、声を掛けて頂ければ嬉しいです。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;今年も PHP カンファレンスは最高でした！これだけの規模のイベントは本当に大変だと思いますが、スタッフの皆さんには感謝するばかりです。今年も楽しいイベントをありがとうございました。&lt;/p&gt;

&lt;p&gt;来年は、12 月ですね。冬の PHP カンファレンスは個人的には初めてな気がするので、どんな感じにするのか楽しみです。&lt;/p&gt;

&lt;h2&gt;さいごに 2&lt;/h2&gt;

&lt;p&gt;せっかくカンファレンス熱が高まったところなので、次回の PHP の現場では技術系カンファレンスについて話す予定です。お楽しみに。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=https%3A%2F%2Fphp-genba.shin1x1.com%2F&quot; title=&quot;PHPの現場&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://php-genba.shin1x1.com/&quot;&gt;php-genba.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20171011/20171011023925.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>PHP カンファレンス関西 2017 にて基調講演をしました</title>
        <link href="https://blog.shin1x1.com/entry/2017/07/18/000000"/>
        <id>hatenablog://entry/8599973812280493708</id>
        <published>2017-07-18T00:00:00+09:00</published>
        <updated>2017-07-18T00:00:11+09:00</updated>        <summary type="html">PHP カンファレンス関西 2017 の基調講演にてお話してきました。 発表資料 今回は、開発現場から見た PHP の特徴をあらためて見直すということで、私が日頃感じている特徴をお話しました。最後には現場で奮闘している PHP エンジニアへのメッセージを入れました。 朝一番のセッションにも関わらず、立ち見が出るほど多くの方に参加頂き、嬉しい限りでした。参加頂いたみなさん、ありがとうございました。 資料だけでは伝わらない部分もありますが、資料を上げておきます。 終わり際にある「繋ぐ言語」というのは、2014 年基調講演された郡山さんの「全てを結ぶ力」へのオマージュだったりします。発表後に会場でご…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;https://2017.kphpug.jp/&quot;&gt;PHP カンファレンス関西 2017&lt;/a&gt; の基調講演にてお話してきました。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;p&gt;今回は、開発現場から見た PHP の特徴をあらためて見直すということで、私が日頃感じている特徴をお話しました。最後には現場で奮闘している PHP エンジニアへのメッセージを入れました。&lt;/p&gt;

&lt;p&gt;朝一番のセッションにも関わらず、立ち見が出るほど多くの方に参加頂き、嬉しい限りでした。参加頂いたみなさん、ありがとうございました。&lt;/p&gt;

&lt;p&gt;資料だけでは伝わらない部分もありますが、資料を上げておきます。&lt;/p&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;67fba795aa834f1ab40e23dbe385f950&quot; data-ratio=&quot;1.33333333333333&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;終わり際にある「繋ぐ言語」というのは、2014 年基調講演された郡山さんの「全てを結ぶ力」へのオマージュだったりします。発表後に会場でご本人に伝えるとがっちり握手して頂けました :)&lt;/p&gt;

&lt;h2&gt;基調講演&lt;/h2&gt;

&lt;p&gt;これまで、招待講演も基調講演を招く側も経験していたのですが、基調講演で話すというのは初めての経験でした。&lt;/p&gt;

&lt;p&gt;はじめは、いつもの発表のノリで軽く受けたのですが、いざ準備してみると、あれこれ考えてしまい、そもそも基調講演とは？という根源的な問いかけを自身にしながら模索していました。誰に何を伝えるのかという考えるのかがグルグルと周り、二転三転した結果、今回の内容になりました。&lt;/p&gt;

&lt;p&gt;PHP カンファレンス関西では、過去に 6 人の方に基調講演をして頂いて、セッション担当としてアテンドしていたのですが、これまで皆さんがやってこられた準備や葛藤が分かり、改めて感謝を感じています。ありがとうございました。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;2011 年に PHP カンファレンス関西を立ち上げて以来、第 1 回、第 2 回は実行委員長、以降はスタッフとしてこのイベントに関わってきたので、1 スピーカーとして参加した最初の PHP カンファレンス関西でした。&lt;/p&gt;

&lt;p&gt;こうして自分をはじめ立ち上げ当初にいたメンバーが抜けていっても、ちゃんとイベントが回っているというのは嬉しい限りです。実行委員長のショウノさんをはじめ、スタッフの皆さん、楽しいイベントをありがとうございました！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20170717/20170717203101.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>DI（Dependency Injection）に関するメモ</title>
        <link href="https://blog.shin1x1.com/entry/di-memo"/>
        <id>hatenablog://entry/10328749687249881529</id>
        <published>2017-05-20T14:24:20+09:00</published>
        <updated>2017-05-29T14:44:40+09:00</updated>        <summary type="html">PHPの現場 にて、DI 談義を行うので、頭を整理しておくためのメモです。 DI についてきちんと知りたいのであれば、参照に挙げたリンク先に有用な記事があるので、そちらを参考にして下さい。 PHP を念頭に置いてますが、Java など他言語でも大枠は同じだと思います。この内容は、いずれ整理するかもしれませんし、そのままかもしれません。 DI という言葉 「DI」が差す意味合いが、依存オブジェクトの注入だけなのか、DI コンテナによる注入を含んでいるのか、DIP まで意識しているのかが、人やコンテキストによって違っていそうで、そこを揃えてから議論しないと。— Masashi Shinbara (…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://php-genba.shin1x1.com/&quot;&gt;PHPの現場&lt;/a&gt; にて、DI 談義を行うので、頭を整理しておくためのメモです。&lt;/p&gt;

&lt;p&gt;DI についてきちんと知りたいのであれば、参照に挙げたリンク先に有用な記事があるので、そちらを参考にして下さい。&lt;/p&gt;

&lt;p&gt;PHP を念頭に置いてますが、Java など他言語でも大枠は同じだと思います。この内容は、いずれ整理するかもしれませんし、そのままかもしれません。&lt;/p&gt;

&lt;h2&gt;DI という言葉&lt;/h2&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;「DI」が差す意味合いが、依存オブジェクトの注入だけなのか、DI コンテナによる注入を含んでいるのか、DIP まで意識しているのかが、人やコンテキストによって違っていそうで、そこを揃えてから議論しないと。&lt;/p&gt;&amp;mdash; Masashi Shinbara (@shin1x1) &lt;a href=&quot;https://twitter.com/shin1x1/status/865399101336072192&quot;&gt;May 19, 2017&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;p&gt;DI について話す時に、何を差すのかが異なると話が噛み合わない。そこで、それぞれに名前を付ける。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DI パターン = 依存オブジェクトを注入することを差す。DI コンテナを利用するかどうかは問わない。（GoF の Strategy パターン）&lt;/li&gt;
&lt;li&gt;DI コンテナ = DI パターンを実現するために便利なライブラリ。依存オブジェクトの生成、注入、ライフサイクル管理&lt;a href=&quot;#f-d5d3f34d&quot; name=&quot;fn-d5d3f34d&quot; title=&quot;HTTP リクエスト単位で全てを破棄する PHP では、オブジェクトのライフサイクル管理の重要性は他言語とは異なる。&quot;&gt;*1&lt;/a&gt;を行う。フレームワークに内包されていることもある。&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;DIパターンの適用範囲は広く、PHP でも型宣言（タイプヒント）を用いて型を指定する場合はもちろんのこと、型宣言が無い場合や無名関数を与える場合も DI の一種と言える。&lt;/p&gt;

&lt;p&gt;実際、AngularJS では、型を指定しない（TypeScript など使えば型指定可能）が、DI を利用している。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;「DI = DI コンテナの利用」ではない。DI コンテナを使わなくても、DI パターンの実現は可能。&lt;/li&gt;
&lt;li&gt;型指定が無くても、DI できる。&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;DI パターン&lt;/h2&gt;

&lt;p&gt;DI パターンでは、依存オブジェクトを型で指定するのが一般的だ。型の指定は、下記の順で、より結合度が低くなり、依存が明確になる。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;型指定無し&lt;/li&gt;
&lt;li&gt;具象クラス&lt;/li&gt;
&lt;li&gt;抽象クラス&lt;/li&gt;
&lt;li&gt;インターフェイス&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;このあたりは、DI に限らず、インターフェイスをなぜ利用するのか？具象クラスだけで良いのでは？という議論にも発展できる。&lt;/p&gt;

&lt;h3&gt;DI パターンのメリット&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;クラス間を疎結合にする。（結合度を下げる。）&lt;/li&gt;
&lt;li&gt;それぞれが独立したモジュールになり、自身の役割のみに専念できる。（凝集度が高くなる。）&lt;/li&gt;
&lt;li&gt;クラスの役割が明確で、限られたものになるので、実装しやすく、テストもしやすく、利用しやすくなる。（脳に優しい）&lt;/li&gt;
&lt;li&gt;必要な依存が明確になる。&lt;/li&gt;
&lt;li&gt;インターフェイスに依存した場合、インターフェイスの API のみが依存箇所なので、それ以外の部分（API の実装や与えられたインスタンスが持つ他のメソッドなど）は一切気にする必要がなくなる。（脳に優しい）&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;DI パターンのデメリット&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ファイルが増える。&lt;/li&gt;
&lt;li&gt;実装を追いにくくなる。（結局は実装読まないと分からないでしょ）&lt;/li&gt;
&lt;li&gt;インターフェイスに依存とか言ってるけど、PHP だと与えられたインスタンスのメソッドは何でも呼べてしまう。（IDEでは警告は出るが、実行はできる）&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;依存オブジェクトの注入&lt;/h3&gt;

&lt;p&gt;依存オブジェクトを注入するには、下記のような方法がある。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;コンストラクタインジェクション&lt;/li&gt;
&lt;li&gt;セッターインジェクション（インターフェイスインジェクション）&lt;/li&gt;
&lt;li&gt;フィールドインジェクション&lt;/li&gt;
&lt;li&gt;メソッドインジェクション&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;どの方法でも実現はできるが、コンストラクタインジェクションが推奨されている。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;インスタンス生成のシーケンスで依存オブジェクトを注入できる。特殊な操作が不要。&lt;/li&gt;
&lt;li&gt;インスタンスが生成されたときには、依存オブジェクトは注入済なので、安全に利用できる。&lt;/li&gt;
&lt;li&gt;コンストラクタでのみ注入できるので、インスタンスにどのような操作を行っても、外部から依存オブジェクトを差し替えられて、想定外の動きをするなどを防ぐことができる。&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;このあたりの利点は、完全コンストラクタとも似ている。&lt;/p&gt;

&lt;h2&gt;DI コンテナ&lt;/h2&gt;

&lt;p&gt;依存オブジェクトをアプリケーション自ら与えることで、DI パターンは実現できるが、実際のところ、DI コンテナを利用するのが一般的だ。&lt;/p&gt;

&lt;p&gt;PHPには、数多くの DI コンテナがある。Web アプリケーションフレームワークを利用している場合、内包されていることが多く、意識せずに利用しているかもしれない。&lt;/p&gt;

&lt;p&gt;PHP の主な DI コンテナは以下。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pimple.sensiolabs.org/&quot;&gt;Pimple - A simple PHP Dependency Injection Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/illuminate/container&quot;&gt;illuminate/container: [READ ONLY] Subtree split of the Illuminate Container component (see laravel/framework)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/auraphp/Aura.Di/blob/3.x/docs/index.md&quot;&gt;Aura.Di/index.md at 3.x · auraphp/Aura.Di&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ray-di/Ray.Di&quot;&gt;ray-di/Ray.Di: Guice style dependency injection framework for PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zendframework.github.io/zend-servicemanager/&quot;&gt;zend-servicemanager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.zendframework.com/zend-di/&quot;&gt;zend-di&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://php-di.org/&quot;&gt;PHP-DI - The Dependency Injection Container for humans&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;PHP では、PSR-11 にて DI コンテナの API が規定されている。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md&quot;&gt;fig-standards/PSR-11-container.md at master · php-fig/fig-standards&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;ワイヤリング&lt;/h3&gt;

&lt;p&gt;DI コンテナは、依存オブジェクトの生成（取得）方法を知っておく必要がある。これには、ファクトリ（クラスや無名関数等）や設定ファイル（YAML や XML）を利用する。&lt;/p&gt;

&lt;p&gt;ただ、全ての生成方法を指定していくのは骨が折れる作業なので、あらかじめ決められたルールや最低限の設定で自動でオブジェクトを生成する auto-wired という仕組みがある。&lt;/p&gt;

&lt;p&gt;例えば、Laravel の DI コンテナでは、型宣言に具象クラスを指定すれば、ワイヤリングの設定が無くとも、new でインスタンスを生成する。また、インターフェイスの場合は、対応する具象クラスを設定しておけば、そのインスタンスを生成する。&lt;/p&gt;

&lt;h3&gt;DI コンテナとサービスロケータ&lt;/h3&gt;

&lt;p&gt;DI コンテナと比較して良く登場するのが、サービスロケータだ。これは、オブジェクトコンテナを依存として注入し、利用時に必要な依存オブジェクトをこのコンテナから取り出すというパターンである。&lt;/p&gt;

&lt;p&gt;サービスロケータは、DI を使わずに依存オブジェクトを内部で生成するパターンの発展系で、一方、DI コンテナは、外部から依存オブジェクトを与えるので、制御の方向が逆とも言える。&lt;/p&gt;

&lt;p&gt;サービスロケータでも、DI コンテナと同様に、実装の差し替えや依存オブジェクトの生成やライフサイクルの管理といったメリットを享受できる。&lt;/p&gt;

&lt;p&gt;ただ、DI パターンのメリットである、依存を明確にする、最小限にするという効果は薄れる。&lt;/p&gt;

&lt;p&gt;このあたりは、何を求めて利用するのかという目的によって使い分けると良い。（サービスロケータが全てダメとかいう短絡的な話ではない）&lt;/p&gt;

&lt;h2&gt;PHP で DI が必要か？&lt;/h2&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;PHPでDI流行ってるの、便利なんだけどもやもやはしている。もっと密結合でいいじゃん、という。&lt;/p&gt;&amp;mdash; Hiroyuki Yamaoka 🐰 (@hiro_y) &lt;a href=&quot;https://twitter.com/hiro_y/status/865212176365109249&quot;&gt;May 18, 2017&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;p&gt;収録分を公開しました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://php-genba.shin1x1.com/7&quot;&gt;7. PHP に DI は必要か？（hiro_y / ytake） | PHPの現場&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;参照&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://kakutani.com/trans/fowler/injection.html&quot;&gt;Inversion of Control コンテナと Dependency Injection パターン&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.a-way-out.net/blog/2015/08/31/your-dependency-injection-is-wrong-as-I-expected/&quot;&gt;やはりあなた方のDependency Injectionはまちがっている。 — A Day in Serenity (Reloaded) — PHP, FuelPHP, Linux or something&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://phpmentors.jp/post/28548053449/pimple%E3%81%A7%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AB%E6%AD%A3%E3%81%97%E3%81%8Fdi%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B&quot;&gt;PHP Mentors -&gt; Pimpleでシンプルに正しくDIを理解する&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://koriym.github.io/adv10/&quot;&gt;Advent Day 10: DI &gt; SL ? « BEAR Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ja.phptherightway.com/#dependency_injection&quot;&gt;PHP: The Right Way&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-d5d3f34d&quot; name=&quot;f-d5d3f34d&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;HTTP リクエスト単位で全てを破棄する PHP では、オブジェクトのライフサイクル管理の重要性は他言語とは異なる。&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>WordPress Way を知る本  「エンジニアのための WordPress 開発入門」</title>
        <link href="https://blog.shin1x1.com/entry/wordpress-guide-for-php-engineer"/>
        <id>hatenablog://entry/10328749687225436996</id>
        <published>2017-03-10T00:00:00+09:00</published>
        <updated>2017-10-23T21:06:59+09:00</updated>        <summary type="html">著者の杉田さんに献本して頂いたので拝読しました。 PHP エンジニアのための WordPress 本 タイトルにあるとおり、普段フレームワークなどを使って Web システムを開発している PHP エンジニアのための WordPress 本です。 ユーザとして WordPress を利用するのではなく、PHP エンジニアとしてどのように扱うのか、どのように付き合うのか視点で書かれているので、興味深く読むことができました。 序盤で、おおっと思ったのが、第 2 章です。WordPress のインストールにの次に紹介されているのが、いきなり、WP_DEBUG をはじめとしたデバッグモードに関する定数、…</summary>
        <content type="html">&lt;p&gt;著者の杉田さんに献本して頂いたので拝読しました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4774187062/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51OEeeO3WoL.jpg&quot; alt=&quot;エンジニアのためのWordPress開発入門 (Engineer&#39;s Library)&quot; title=&quot;エンジニアのためのWordPress開発入門 (Engineer&#39;s Library)&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;PHP エンジニアのための WordPress 本&lt;/h2&gt;

&lt;p&gt;タイトルにあるとおり、普段フレームワークなどを使って Web システムを開発している PHP エンジニアのための WordPress 本です。&lt;/p&gt;

&lt;p&gt;ユーザとして WordPress を利用するのではなく、PHP エンジニアとしてどのように扱うのか、どのように付き合うのか視点で書かれているので、興味深く読むことができました。&lt;/p&gt;

&lt;p&gt;序盤で、おおっと思ったのが、第 2 章です。WordPress のインストールにの次に紹介されているのが、いきなり、WP_DEBUG をはじめとしたデバッグモードに関する定数、Debug Bar プラグイン、コマンドラインツールの WP-CLI、そして開発用仮想環境を構築する VCCW という流れになっています。&lt;/p&gt;

&lt;p&gt;PHP エンジニアが、WordPress を触るための環境構築といった感じで、このあたりからも誰に向けた本なのかが分かります。&lt;/p&gt;

&lt;p&gt;個人的には、さらに PhpStorm 上で WordPress を触るための設定などに進んでいくと、グッときますね :)&lt;/p&gt;

&lt;h2&gt;第5章 WordPress の基本アーキテクチャ&lt;/h2&gt;

&lt;p&gt;本書のキモとなるのは、第5章の「WordPressの基本アーキテクチャ」でしょう。&lt;/p&gt;

&lt;p&gt;WordPress のファイル構成、データ構成（データベーステーブル）、基本処理の流れ、HTTP リクエストパラメータの扱い、フックの仕組みなどベースとなるアーキテクチャが解説されています。&lt;/p&gt;

&lt;p&gt;これらを知らずとも WordPress は利用できまずが、ブラックボックスとして扱うより、基本的な流れを把握して利用すれば動作を想像することができ、問題解決やプラグインによる拡張を行う際に大きな助けとなるでしょう。&lt;/p&gt;

&lt;p&gt;まず、この章を読んで全体を把握してから、知りたい箇所を各章で補っていくのが良さそうですね。&lt;/p&gt;

&lt;h2&gt;WordPress と向き合うための本&lt;/h2&gt;

&lt;p&gt;WordPress は、歴史の長いプロダクトのため、現在行われている PHP 開発とは異なる流儀でコードが実装されています。&lt;/p&gt;

&lt;p&gt;コードを読んだ人がまず感じることが、まさに本文に書かれています。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;ところが、率直のところ、WordPress のソースコードは美しいとはとても言えません。重要なメソッドや関数が恐ろしいほどの行数だったり、驚くほどたくさんのグローバル変数が定義されていたりします。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;こうした WordPress のコードを見た時に感じた違和感をはっきりと明言しておき、それを前提としてどう向き合うのかという視点が本書のユニークなところです。&lt;/p&gt;

&lt;p&gt;これは本書に随所で見られる視点で、WP_Query が 4,000 行超あることが書かれていたり、サンプルコードに global キーワードがあったり、グローバル変数一覧がまとめられていたりします。&lt;/p&gt;

&lt;p&gt;「言いたいことは分かった。ただ、WordPress のコードを向き合うなら、こうしよう。」&lt;/p&gt;

&lt;p&gt;と言ってくれているような気がします。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;「郷に入っては郷に従え」というのは、フレームワークでも言語でもプラットフォームでも良く言われていることです。&lt;/p&gt;

&lt;p&gt;WordPress で開発するなら、まずは WordPress Way に従うのが良い方法で、本書にはその方法がまとめられています。WordPress Way を知るには大きな足がかりになる貴重な本です。&lt;a href=&quot;#f-4483536b&quot; name=&quot;fn-4483536b&quot; title=&quot;個人的には、WordPress はブラックボックスにして、WP REST API で触るというのも面白いと思いますが、その視点でも本書は役立ちます。&quot;&gt;*1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;開発で WordPress を活用する人はもちろんのこと、WordPress の内部構造や動きに興味がある方にもおすすめです。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4774187062/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51OEeeO3WoL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;エンジニアのためのWordPress開発入門 (Engineer&#39;s Library)&quot; title=&quot;エンジニアのためのWordPress開発入門 (Engineer&#39;s Library)&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4774187062/shinxblog-22/&quot;&gt;エンジニアのためのWordPress開発入門 (Engineer&#39;s Library)&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; 野島祐慈,菱川拓郎,杉田知至,細谷崇,枢木くっくる&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 技術評論社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2017/01/26&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本（ソフトカバー）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4774187062/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログを見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-4483536b&quot; name=&quot;f-4483536b&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;個人的には、WordPress はブラックボックスにして、WP REST API で触るというのも面白いと思いますが、その視点でも本書は役立ちます。&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://images-fe.ssl-images-amazon.com/images/I/51OEeeO3WoL._SL160_.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「PHPの現場」という Podcast をはじめます</title>
        <link href="https://blog.shin1x1.com/entry/php-genba-podcast"/>
        <id>hatenablog://entry/10328749687222404293</id>
        <published>2017-03-02T00:00:00+09:00</published>
        <updated>2017-06-11T19:09:19+09:00</updated>        <summary type="html">学生の頃からラジオを聞いて育ってきたので、技術系のPodcastが好きで色々と聞いています。 日本語で PHP 界隈の話題について話すPodcastが無かったので、はじめてみることにしました。 「PHPの現場」というPodcastです。 PHP がメイントピックにはなりますが、PHP の開発現場で出てきそうな話題なら、それ以外の技術や話題についても扱っていくつもりです。 私がお話したいゲストの方をむかえて対談（雑談）形式でざっくばらんに話していきたいので、色々な方にお声がけしていこうと思います。その際は、よろしくお願いしますm(_ _)m Podcast配信環境 Podcastを配信する上で、…</summary>
        <content type="html">&lt;p&gt;学生の頃からラジオを聞いて育ってきたので、技術系のPodcastが好きで色々と聞いています。
日本語で PHP 界隈の話題について話すPodcastが無かったので、はじめてみることにしました。&lt;/p&gt;

&lt;p&gt;「PHPの現場」というPodcastです。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fphp-genba.shin1x1.com%2F&quot; title=&quot;PHPの現場&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;p&gt;PHP がメイントピックにはなりますが、PHP の開発現場で出てきそうな話題なら、それ以外の技術や話題についても扱っていくつもりです。&lt;/p&gt;

&lt;p&gt;私がお話したいゲストの方をむかえて対談（雑談）形式でざっくばらんに話していきたいので、色々な方にお声がけしていこうと思います。その際は、よろしくお願いしますm(_ _)m&lt;/p&gt;

&lt;h2&gt;Podcast配信環境&lt;/h2&gt;

&lt;p&gt;Podcastを配信する上で、どうしようかと迷ったのは配信環境です。&lt;/p&gt;

&lt;p&gt;SoundCloud などのサービスを利用することも考えたのですが、どうせなら作ってしまおうと思い、自作しました。（実は、過去にPodcastの配信を行ったことがあり、その時も自作（GAE+Python）していました。）&lt;/p&gt;

&lt;p&gt;Podcast配信サーバは、ようは RSS フィード配信とオーディオファイル配信（Range Requests 対応）ができれば良いので実装は難しくありません。ただ、配信 RSS の内容を調整したり、どうせ趣味で作るならとあれこれいじっていたら、思ったより時間がかかってしまいました。&lt;/p&gt;

&lt;p&gt;音源はあるのに、配信サーバの調整に時間をかけるという本末転倒な状況でしたが、久しぶりに趣味の Web サービスを作ったので、空き時間に楽しんで作業していました :) 自由にできるアプリケーションがあるのはいいですね。&lt;/p&gt;

&lt;p&gt;技術要素としては、以下のような感じです。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Digital Ocean&lt;/li&gt;
&lt;li&gt;Docker / docker-compose&lt;/li&gt;
&lt;li&gt;PHP 7.1 + Slim 3&lt;/li&gt;
&lt;li&gt;papertrail（サーバ、Docker コンテナのログを集約）&lt;/li&gt;
&lt;li&gt;Google Analytics（Web アクセス、RSS / オーディオファイルダウンロード実績を集約）&lt;/li&gt;
&lt;li&gt;Mackerel（サーバ監視）&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;開発でも本番でも docker-compose でコンテナを起動するだけなので、インフラはその時々の状況でよしなに移動できます。&lt;/p&gt;

&lt;p&gt;今回の実装でアプリケーション仕様は固まったので、今後は気が向けば別言語で再実装して遊ぶのもいいですね。&lt;/p&gt;

&lt;h2&gt;初回配信&lt;/h2&gt;

&lt;p&gt;初回は、下記エントリに書いた「Podcast で話すような感じでやる発表」を実現した昨年末の関西 PHP 勉強会の模様を配信しています。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;https://hatenablog-parts.com/embed?url=http%3A%2F%2Fblog.shin1x1.com%2Fentry%2Fstudy-session-like-a-podcast-recording&quot; title=&quot;公開 Podcast のような勉強会セッションをやってみる - Shin x Blog&quot; class=&quot;embed-card embed-blogcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://blog.shin1x1.com/entry/study-session-like-a-podcast-recording&quot;&gt;blog.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;@tanakahisateru さんと例外について話しました。&lt;/p&gt;

&lt;p&gt;この回は、年末ということもあり、乾杯の後に発表をはじめた、くだけた雰囲気でした。途中、二人以外にも話に入ってきたりして、楽しかったです。配信を聞かれる方は、そういった雰囲気での話というのをイメージして下さい :)&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;まだ手探り感が満載ですが、はじめて見ないことには分からないので、少しづつやっていこうと思います。&lt;/p&gt;

&lt;p&gt;感想や要望などあれば、 #phpgenba で tweet して頂けると嬉しいです。&lt;/p&gt;

&lt;p&gt;それでは、「PHPの現場」よろしくおねがいしますー！&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fphp-genba.shin1x1.com%2F&quot; title=&quot;PHPの現場&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;itunes-embed freezed itunes-kind-podcast&quot;&gt;&lt;a href=&quot;https://itunes.apple.com/jp/podcast/php%E3%81%AE%E7%8F%BE%E5%A0%B4/id1210077076?mt=2&amp;uo=4&amp;at=10l8JW&amp;ct=hatenablog&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;http://cdn.image.st-hatena.com/image/scale/a4add27aaad10e9bc632671efbf42003eb54a44b/enlarge=0;height=200;version=1;width=200/http%3A%2F%2Fis4.mzstatic.com%2Fimage%2Fthumb%2FMusic122%2Fv4%2Fc7%2F0f%2F90%2Fc70f90eb-59b2-6974-a7a4-ce5152ae0340%2Fsource%2F100x100bb.jpg&quot; alt=&quot;PHPの現場&quot; title=&quot;PHPの現場&quot; class=&quot;itunes-embed-image&quot;/&gt;&lt;/a&gt;&lt;div class=&quot;itunes-embed-info&quot;&gt;&lt;p class=&quot;itunes-embed-title&quot;&gt;&lt;a href=&quot;https://itunes.apple.com/jp/podcast/php%E3%81%AE%E7%8F%BE%E5%A0%B4/id1210077076?mt=2&amp;uo=4&amp;at=10l8JW&amp;ct=hatenablog&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;PHPの現場&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li class=&quot;itunes-embed-artist&quot;&gt;Masashi Shinbara&lt;/li&gt;&lt;li class=&quot;itunes-embed-genre&quot;&gt;テクノロジー&lt;/li&gt;&lt;li class=&quot;itunes-embed-price&quot;&gt;¥0&lt;/li&gt;&lt;li class=&quot;itunes-embed-badge&quot;&gt;&lt;a href=&quot;https://itunes.apple.com/jp/podcast/php%E3%81%AE%E7%8F%BE%E5%A0%B4/id1210077076?mt=2&amp;uo=4&amp;at=10l8JW&amp;ct=hatenablog&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://cdn.blog.st-hatena.com/images/theme/itunes/itunes-badge-itunes@2x.png&quot; width=&quot;44px&quot; height=&quot;15px&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20170302/20170302103313.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>openssl_encrypt() による PKCS#7 パディング</title>
        <link href="https://blog.shin1x1.com/entry/openssl-add-pkcs7-padding"/>
        <id>hatenablog://entry/10328749687205937897</id>
        <published>2017-01-12T10:40:00+09:00</published>
        <updated>2017-01-12T12:00:35+09:00</updated>        <summary type="html">PHP の openssl_encrypt() にて、ブロック暗号による暗号化を行うと PKCS#7 パディングが行われます。この動きを確認してみます。 http://php.net/manual/ja/function.openssl-encrypt.php PKCS#7 パディング ブロック暗号では、決められたブロック長を単位として暗号化を行います。対象となる平文がブロック長の倍数の長さであれば良いのですが、そうではない場合、不足分を補う必要があります。これがパディングです。*1 PKCS#7 パディングは RFC 5652 で定義されたパディング方式です。補ったバイト数を 1 バイトの値…</summary>
        <content type="html">&lt;p&gt;PHP の openssl_encrypt() にて、ブロック暗号による暗号化を行うと PKCS#7 パディングが行われます。この動きを確認してみます。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://php.net/manual/ja/function.openssl-encrypt.php&quot;&gt;http://php.net/manual/ja/function.openssl-encrypt.php&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;PKCS#7 パディング&lt;/h2&gt;

&lt;p&gt;ブロック暗号では、決められたブロック長を単位として暗号化を行います。対象となる平文がブロック長の倍数の長さであれば良いのですが、そうではない場合、不足分を補う必要があります。これがパディングです。&lt;a href=&quot;#f-101eafb7&quot; name=&quot;fn-101eafb7&quot; title=&quot;実際には、後述するとおりブロック長倍数であってもパディングが追加されます。&quot;&gt;*1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PKCS#7 パディングは RFC 5652 で定義されたパディング方式です。補ったバイト数を 1 バイトの値として埋めます。例えば、3 バイトを補うのであれば、&lt;code&gt;0xXXXX030303&lt;/code&gt; のように &lt;code&gt;3&lt;/code&gt; が 3 つ並びます。PKCS#7 では、1 バイトで不足バイト数を示すので、1 - 255 バイトまでを補うことができます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;01     &amp;lt;--- 1 バイト分
0202   &amp;lt;--- 2 バイト分
030303 &amp;lt;--- 3 バイト分&lt;/pre&gt;


&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7&quot;&gt;https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;これと似た動きをするのが、PKCS#5 パディングで、こちらは 8 バイトを倍数とした場合のパディングを行います。つまり、取り得る値は 1 - 8 となります。&lt;/p&gt;

&lt;p&gt;余談ですが、Java の javax.crypto.Cipher にある &lt;code&gt;PKCS5Padding&lt;/code&gt; は、名前は PKCS5 ですが、実質は PKCS#7 相当の動きをするようです。&lt;/p&gt;

&lt;p&gt;他のパディングには、0 で埋める ゼロパディングなどがあります。&lt;/p&gt;

&lt;h2&gt;openssl_encrypt() によるパディング&lt;/h2&gt;

&lt;p&gt;openssl_encrypt() のデフォルトでは、PKCS#7 パディングが行われます。&lt;/p&gt;

&lt;p&gt;この動きを見ていくためのサンプルが以下です。このサンプルでは、openssl_encrypt() で暗号化、openssl_decrypt()で復号を行っています。&lt;/p&gt;

&lt;p&gt;ここでは、AES（Rijndael）を利用しているのでブロック長は 128bit（16バイト）です。平文は &lt;code&gt;plain&lt;/code&gt; の 5 バイトなので、不足分の 11 バイトがパディングで埋められます。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;plaintext&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;plain&lt;/span&gt;&#39;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; hash&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;sha256&lt;/span&gt;&#39;, &#39;&lt;span class=&quot;synConstant&quot;&gt;this is a secret key.&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;aes-256-cbc&lt;/span&gt;&#39;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_random_pseudo_bytes&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;openssl_cipher_iv_length&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_encrypt&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;plaintext&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synIdentifier&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;synConstant&quot;&gt;encrypted: %s&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;\n&lt;/span&gt;&amp;quot;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;decrypted&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_decrypt&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synIdentifier&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;synConstant&quot;&gt;decrypted: %s&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;\n&lt;/span&gt;&amp;quot;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;decrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;/pre&gt;


&lt;p&gt;これを実行すると下記のような出力になります。暗号文はデフォルトで base64 エンコードされています。復号した結果を見ると元の平分が出力されています。ここではパディングが見えないですが、これは openssl_decrypt() にて復号時にパディングを除去するためです。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;encrypted: vGZiiLXzuiGnOBkJbWAiHw==
decrypted: plain&lt;/pre&gt;


&lt;p&gt;暗号文にパディングが含まれているかを確認するために復号時に &lt;code&gt;OPENSSL_NO_PADDING&lt;/code&gt; を指定して、パディング除去が行われないようにします。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;plaintext&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;plain&lt;/span&gt;&#39;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; hash&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;sha256&lt;/span&gt;&#39;, &#39;&lt;span class=&quot;synConstant&quot;&gt;this is a secret key.&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;aes-256-cbc&lt;/span&gt;&#39;;
&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_random_pseudo_bytes&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;openssl_cipher_iv_length&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_encrypt&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;plaintext&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synIdentifier&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;synConstant&quot;&gt;size: %d bytes&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;\n&lt;/span&gt;&amp;quot;, &lt;span class=&quot;synIdentifier&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;base64_decode&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)))&lt;/span&gt;;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;decrypted&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; openssl_decrypt&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;base64_decode&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;encrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;method&lt;/span&gt;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;key&lt;/span&gt;, OPENSSL_RAW_DATA &lt;span class=&quot;synStatement&quot;&gt;|&lt;/span&gt; OPENSSL_NO_PADDING, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
&lt;span class=&quot;synIdentifier&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;synConstant&quot;&gt;decrypted: %s&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;\n&lt;/span&gt;&amp;quot;, &lt;span class=&quot;synIdentifier&quot;&gt;bin2hex&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;decrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
&lt;span class=&quot;synIdentifier&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;synConstant&quot;&gt;size: %d&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;\n&lt;/span&gt;&amp;quot;, &lt;span class=&quot;synIdentifier&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;decrypted&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
&lt;/pre&gt;


&lt;p&gt;これを実行したのが下記です。&lt;/p&gt;

&lt;p&gt;パディングが見えるように復号した結果は bin2hex() で出力しています。前半の &lt;code&gt;706c61696e&lt;/code&gt; の部分は平文の &lt;code&gt;plain&lt;/code&gt; です。それにつづく &lt;code&gt;0b&lt;/code&gt; が 11 回連続している箇所がパディングです。ブロック長 16 バイトから平文の 5 バイトを引いた 11 バイトがパディングとなるので &lt;code&gt;0x0b&lt;/code&gt; が 11 回連速しています。&lt;/p&gt;

&lt;p&gt;復号した結果は 16 バイトとなっており、これもブロック長と一致します。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;encrypted: iwBv6P3euwwdct8DHutBNA==
decrypted: 706c61696e0b0b0b0b0b0b0b0b0b0b0b
size: 16&lt;/pre&gt;


&lt;h2&gt;平文サイズがブロック長と一致している場合&lt;/h2&gt;

&lt;p&gt;では、平文サイズがブロック長と一致している場合はどうなるでしょう。&lt;/p&gt;

&lt;p&gt;平文を &lt;code&gt;plainplainplaina&lt;/code&gt; の 16 バイトにして実行したのが下記です。&lt;/p&gt;

&lt;p&gt;復号結果は、なんと 32 バイトになりました。内容を見ると &lt;code&gt;10&lt;/code&gt; が 16 回連続しています。これは、ブロック長分のパディングです。つまり、1 ブロック分が追加されているわけです。&lt;/p&gt;

&lt;p&gt;平文の後にはパディングが付いていることを前提とするため（そうでないと平文なのかパディングなのか判断できない）にこのような動きになっているのでしょう。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;encrypted: PFZ8G+XYhs9x22aP/a7Jy88eQJv6GVaKMXrBfq5/bGU=
decrypted: 706c61696e706c61696e706c61696e6110101010101010101010101010101010
size: 32&lt;/pre&gt;


&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;openssl_encrypt() によるパディングを見てきました。&lt;/p&gt;

&lt;p&gt;デフォルトのまま、openssl_encrypt() で暗号化し、openssl_decrypt() で復号するならパディングを意識する必要はあまりありません。&lt;/p&gt;

&lt;p&gt;しかし、別システムで暗号化されたものを復号したり、その逆の場合は、どのような形式でパディングを行うのかというのは共有しておく必要があります。&lt;/p&gt;

&lt;h2&gt;参照&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html&quot;&gt;https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-101eafb7&quot; name=&quot;f-101eafb7&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;実際には、後述するとおりブロック長倍数であってもパディングが追加されます。&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20170112/20170112103348.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>アプリケーションから例外を投げる派、投げない派</title>
        <link href="https://blog.shin1x1.com/entry/application-throws-exception-or-not"/>
        <id>hatenablog://entry/10328749687201423811</id>
        <published>2016-12-27T00:37:24+09:00</published>
        <updated>2016-12-29T01:55:19+09:00</updated>        <summary type="html">例外をどのような状況に投げるかもしくは投げないか、というのはわりと意見が分かれるところです。もちろん、プログラミング言語によっても異なりますが、同じプログラミング言語ユーザ同士でも様々です。 基本の考え方 ベースとしては、Effective Java の項目 39 にある下記の方針が参考になります。 例外的な状況の時にのみ例外を使う。 Effective Java 禅問答のような定義ですが、これには異論は無いでしょう。例外を正常フローで利用したり、制御構造に用いるべきではありません。 人によって異なるのは「例外的な状況」の解釈です。 例外的な状況 この「例外的な状況」の解釈は人によって異なるよ…</summary>
        <content type="html">&lt;p&gt;例外をどのような状況に投げるかもしくは投げないか、というのはわりと意見が分かれるところです。もちろん、プログラミング言語によっても異なりますが、同じプログラミング言語ユーザ同士でも様々です。&lt;/p&gt;

&lt;h2&gt;基本の考え方&lt;/h2&gt;

&lt;p&gt;ベースとしては、Effective Java の項目 39 にある下記の方針が参考になります。&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;例外的な状況の時にのみ例外を使う。&lt;/p&gt;

&lt;p&gt;Effective Java&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;禅問答のような定義ですが、これには異論は無いでしょう。例外を正常フローで利用したり、制御構造に用いるべきではありません。&lt;/p&gt;

&lt;p&gt;人によって異なるのは「例外的な状況」の解釈です。&lt;/p&gt;

&lt;h2&gt;例外的な状況&lt;/h2&gt;

&lt;p&gt;この「例外的な状況」の解釈は人によって異なるようで、これまでも議論になっていました。これまで聞いた解釈を乱暴に分けると以下の 2 パターンに分かれます。&lt;/p&gt;

&lt;h3&gt;1. アプリケーションから独自の例外を投げる派&lt;/h3&gt;

&lt;p&gt;ランタイムやミドルウェア連携などプラットフォーム起因のエラーだけではなく、 アプリケーションやドメインにおけるエラーを例外としてスローする派です。例外クラスも必要に応じて定義します。&lt;/p&gt;

&lt;p&gt;例外をエラー処理に積極的に利用する考え方ともいえ、例えば、入力値におけるバリデーションエラーやドメインロジックのエラーにおいても例外を利用します。&lt;/p&gt;

&lt;h3&gt;2. プラットフォームやフレームワークが投げる例外以外は使わない派&lt;/h3&gt;

&lt;p&gt;ランタイムやミドルウェア連携などプラットフォーム起因のエラーなど深刻なエラーでのみ例外を利用し、アプリケーションにおけるエラーにおいては例外を利用しない考え方です。&lt;/p&gt;

&lt;p&gt;この考え方の人の中には、例外クラスはアプリケーション独自で定義せず、アプリケーションからは例外をスローしない（ランタイムやフレームワーク、ライブラリからの例外のみ扱う）という意見もありました。&lt;/p&gt;

&lt;p&gt;アプリケーションのエラーは、戻り値で通知することになります。（特殊な値（null や false）を使ったり、原因別に戻り値を返すなど）&lt;/p&gt;

&lt;h2&gt;アプリケーションから独自の例外を投げる派の意見&lt;/h2&gt;

&lt;p&gt;私は、前者の「例外を積極的に使う派」です。理由としては、下記のようなメリットを感じています。
（なお、ここでは PHP を対象として、検査例外のことは考えていません。）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[実装][利用] エラー処理を例外に統一できる。&lt;/li&gt;
&lt;li&gt;[利用] 呼び出す側は例外だけ対応すれば良い。（catch する / 対応しない（上位にスロー））&lt;/li&gt;
&lt;li&gt;[利用] どのレイヤでエラー処理するかを呼び出し側が選べる。（上位レイヤに伝播できるので、フレームワークの統一的なエラー処理に任せてしまうなどが簡単）&lt;/li&gt;
&lt;li&gt;[実装] エラー時は例外を投げれば良いので、ガード節の代わりにもなる。&lt;/li&gt;
&lt;li&gt;[実装] テストでは、@expectedException でテストを書けば良いので、何のテストか分かりやすい。（正常時は例外飛ばないので）&lt;/li&gt;
&lt;li&gt;[利用] try / catch を書けば、try の中は正常系、catch の中は異常系と処理が分かれて分かりやすい。&lt;/li&gt;
&lt;li&gt;[利用] 戻り値のエラー判定が不要。例外が飛ばなければ正常終了と判断できる。&lt;/li&gt;
&lt;li&gt;[利用] 例外クラスが持つ情報量が多い。（特にスタックトレース）&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;例外については、人それぞれ思いがあるようで、議論になりやすいところです。この機会に色々な人の意見を聞いてみたいですね。&lt;/p&gt;

&lt;p&gt;今日開催の関西 PHP 勉強会でも、話しましょう。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://kphpug.doorkeeper.jp/events/55554&quot;&gt;https://kphpug.doorkeeper.jp/events/55554&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4621066056/shinxblog-22/&quot;&gt;&lt;img src=&quot;https://images-fe.ssl-images-amazon.com/images/I/51lEBnUjJqL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;EFFECTIVE JAVA 第2版 (The Java Series)&quot; title=&quot;EFFECTIVE JAVA 第2版 (The Java Series)&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4621066056/shinxblog-22/&quot;&gt;EFFECTIVE JAVA 第2版 (The Java Series)&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; Joshua Bloch,柴田芳樹&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 丸善出版&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2014/03/11&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本（ソフトカバー）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4621066056/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (10件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;h2&gt;追記&lt;/h2&gt;

&lt;p&gt;Twitter へ頂いたコメントをまとめました。色々な意見がありますね。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Ftogetter.com%2Fli%2F1063954&quot; title=&quot;「アプリケーションから例外を投げる派、投げない派」へのコメント&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://togetter.com/li/1063954&quot;&gt;togetter.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;はてぶでも多くのコメントがあったので、こちらも追記。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fb.hatena.ne.jp%2Fentry%2Fblog.shin1x1.com%2Fentry%2Fapplication-throws-exception-or-not&quot; title=&quot;はてなブックマーク - アプリケーションから例外を投げる派、投げない派 - Shin x Blog&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://b.hatena.ne.jp/entry/blog.shin1x1.com/entry/application-throws-exception-or-not&quot;&gt;b.hatena.ne.jp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161227/20161227002255.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>子どもの iPhone SE に行った設定リスト 2016年12月</title>
        <link href="https://blog.shin1x1.com/entry/iphone-se-settings-for-kids"/>
        <id>hatenablog://entry/10328749687200245396</id>
        <published>2016-12-22T00:00:00+09:00</published>
        <updated>2016-12-22T12:56:56+09:00</updated>        <summary type="html">子ども（13 才以上）用に iPhone を購入したので、セットアップしたメモです。 iPhone IIJmio を利用するために、Apple Store で SIM フリーの iPhone SE を購入しました。 www.apple.com SIM 私が、IIJmio を利用していて、何ら不都合が無いので、同じく IIJmio にしました。連絡は、LINE（チャット or 電話）で行うつもりなので、データ通信専用 SIM を申し込みました。 3GB プランで、900円/月です。（後述する i-フィルター for マルチデバイス を申込んだので、+ 360円） www.iijmio.jp ペア…</summary>
        <content type="html">&lt;p&gt;子ども（13 才以上）用に iPhone を購入したので、セットアップしたメモです。&lt;/p&gt;

&lt;h2&gt;iPhone&lt;/h2&gt;

&lt;p&gt;IIJmio を利用するために、Apple Store で SIM フリーの iPhone SE を購入しました。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.apple.com%2Fjp%2Fshop%2Fbuy-iphone%2Fiphone-se&quot; title=&quot;iPhone SE&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://www.apple.com/jp/shop/buy-iphone/iphone-se&quot;&gt;www.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;SIM&lt;/h2&gt;

&lt;p&gt;私が、IIJmio を利用していて、何ら不都合が無いので、同じく IIJmio にしました。連絡は、LINE（チャット or 電話）で行うつもりなので、データ通信専用 SIM を申し込みました。&lt;/p&gt;

&lt;p&gt;3GB プランで、900円/月です。（後述する i-フィルター for マルチデバイス を申込んだので、+ 360円）&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.iijmio.jp%2Fhdd%2Fdata%2F&quot; title=&quot;データ通信専用SIM | IIJmio&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://www.iijmio.jp/hdd/data/&quot;&gt;www.iijmio.jp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;ペアレンタルコントロール（機能制限）&lt;/h2&gt;

&lt;p&gt;利用できるアプリやアクセス先を制限できる機能です。必要な箇所を設定しました。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2FHT201304&quot; title=&quot;iPhone、iPad、iPod touch で機能制限を使う&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/HT201304&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;Apple ID&lt;/h2&gt;

&lt;p&gt;利用者が、13 才未満なら子ども用の Apple ID が作成できます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2FHT201084&quot; title=&quot;ファミリー共有とお子様用の Apple ID&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/HT201084&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;今回は、13 才を超えているので、Google アカウントを作成し、Gmail のアドレスで、Apple ID を取得することにしました。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Google アカウントを作成
&lt;a href=&quot;https://accounts.google.com/SignUp?hl=ja&quot;&gt;https://accounts.google.com/SignUp?hl=ja&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apple ID を作成
&lt;a href=&quot;https://appleid.apple.com/account?localang=ja_JP#!&amp;page=create&quot;&gt;https://appleid.apple.com/account?localang=ja_JP#!&amp;page=create&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;インストールするアプリを許可制にする&lt;/h2&gt;

&lt;p&gt;iCloud のファミリー共有を使えば、子どもが自由にアプリをインストールすることができなくなります。
アプリをインストールすると、親（管理者）へ承認を求めるようになります。承認が要求されると、親の iOS デバイスに通知が来て、ここで許可 or 拒否を指定できます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.apple.com%2Fjp%2Ficloud%2Ffamily-sharing%2F&quot; title=&quot;iCloud - ファミリー共有&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://www.apple.com/jp/icloud/family-sharing/&quot;&gt;www.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2Fht201079&quot; title=&quot;家族の購入と支払い - Apple サポート&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/ht201079&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;ファミリー共有すると、家族の端末にインストールしたアプリや現在位置を管理者の端末で確認できます。反対に管理者の情報を家族の端末で見ることもできますが、どの情報を共有するかは ON / OFF できるので、管理者のものは共有しないことも可能です。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2FHT201087&quot; title=&quot;ファミリー共有と位置情報 - Apple サポート&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/HT201087&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;Apple Music&lt;/h2&gt;

&lt;p&gt;Apple Music を家族で利用できるようにします。すでに個人メンバーシップには登録していたので、これをファミリーメンバーシップに切り替えて、子どもの Apple ID を招待しました。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2FHT205595&quot; title=&quot;Apple Music とファミリー共有&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/HT205595&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupport.apple.com%2Fja-jp%2FHT204939%23changetype&quot; title=&quot;iPhone、iPad、iPod touch、Mac、Windows パソコン、Apple TV で Apple Music のメンバーシップを管理する&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/HT204939#changetype&quot;&gt;support.apple.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;LINE&lt;/h2&gt;

&lt;p&gt;LINE アカウントを作成するには電話番号か Facebook アカウントが必要になります。今回は、データ通信のみの SIM にしたので、Facebook アカウントを作成しました。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook
&lt;a href=&quot;https://www.facebook.com&quot;&gt;https://www.facebook.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;LINE アプリをインストールして、「Facebook アカウントでログイン」を選ぶと、Facebook連携でアカウントで LINE アカウントが作成できます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Fofficial-blog.line.me%2Fja%2Farchives%2F20539441.html&quot; title=&quot;FacebookアカウントでLINEの登録・引継ができるよう認証・連携機能を搭載 | LINE公式ブログ&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://official-blog.line.me/ja/archives/20539441.html&quot;&gt;official-blog.line.me&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;あと、意図せず友達申請が来たりするのを防ぐために幾つか設定しておきます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;プロフィール - 「IDで友達追加を許可」を off（年齢確認できていないので、off のはず）&lt;/li&gt;
&lt;li&gt;プライバシー管理 - 「メッセージ受信拒否」を on （友達以外からのメッセージ受信を拒否）&lt;/li&gt;
&lt;li&gt;友だち - 「友だち追加」を off （アドレス帳の連絡先を自動で友だちに追加しない）&lt;/li&gt;
&lt;li&gt;友だち - 「友だちへの追加を許可」を off （電話番号を保有している LINE ユーザが自動で友だち追加したり、検索できるのを拒否）&lt;/li&gt;
&lt;li&gt;タイムライン - タイムラインは使わなくて良いけど、もし使うなら「友だちの公開設定」「新しい友だちに自動公開」を確認。&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;i-フィルター for マルチデバイス&lt;/h2&gt;

&lt;p&gt;フィルタリングソフトを一応導入しました。まだ、どれほど活用できるかは分からないですが、やってみて、不要なら解約する予定です。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.iijmio.jp%2Fhdd%2Foption%2Fifilter%2F&quot; title=&quot;i-フィルター for マルチデバイス | IIJmio&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://www.iijmio.jp/hdd/option/ifilter/&quot;&gt;www.iijmio.jp&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;メールアドレスも無い、Apple ID も無い状態から iPhone を設定したのは、はじめてなので思ったより手間取りました。&lt;/p&gt;

&lt;p&gt;ある程度慣れている人なら調べながら自分でできますが、詳しく無い人だとほんと大変ですね。&lt;/p&gt;

&lt;p&gt;格安 SIM を利用する人が増えていますが、3 キャリアにはこうしたセットアップを店舗でサポートしてくれるサービスがあるので、月々の通信費は割高でも、サポートが必要な人にとってはそちらの方が良いのかもしれません。&lt;/p&gt;

&lt;p&gt;はじめに手にした iPhone 。大事に使って、良い思い出を作っていって欲しいものです。&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161221/20161221153843.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>公開 Podcast のような勉強会セッションをやってみる</title>
        <link href="https://blog.shin1x1.com/entry/study-session-like-a-podcast-recording"/>
        <id>hatenablog://entry/10328749687199834822</id>
        <published>2016-12-20T00:00:00+09:00</published>
        <updated>2017-03-21T14:48:01+09:00</updated>        <summary type="html">勉強会の企画を考えるとき、あの人からあの話聞きたいなというのがあって、普通なら登壇を依頼すればいいんだけど、準備も大変だろうから、あの話の blog エントリや過去のスライドを前に出して、インタビュー形式で「ここ教えて下さい」みたいな感じでやるセッション案。— Masashi Shinbara (@shin1x1) 2016年12月19日 動機 内容が大事で、それが聞きたいだけなのに、スライド作ってとか表現への準備が大変。 技術系勉強会では、スライドを作ることは本質ではない。（内容が伝われば手段は何でも良い） かといって、登壇する時にスライド無しで一人で話すのも大変。 運営としても、発表者とし…</summary>
        <content type="html">&lt;p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;ja&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;勉強会の企画を考えるとき、あの人からあの話聞きたいなというのがあって、普通なら登壇を依頼すればいいんだけど、準備も大変だろうから、あの話の blog エントリや過去のスライドを前に出して、インタビュー形式で「ここ教えて下さい」みたいな感じでやるセッション案。&lt;/p&gt;&amp;mdash; Masashi Shinbara (@shin1x1) &lt;a href=&quot;https://twitter.com/shin1x1/status/810694902912872450&quot;&gt;2016年12月19日&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;/p&gt;

&lt;h2&gt;動機&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;内容が大事で、それが聞きたいだけなのに、スライド作ってとか表現への準備が大変。&lt;/li&gt;
&lt;li&gt;技術系勉強会では、スライドを作ることは本質ではない。（内容が伝われば手段は何でも良い）&lt;/li&gt;
&lt;li&gt;かといって、登壇する時にスライド無しで一人で話すのも大変。&lt;/li&gt;
&lt;li&gt;運営としても、発表者としても前々から解決したいテーマだった。&lt;/li&gt;
&lt;li&gt;Podcast は、音声だけだが、十二分に伝わる。緩い雰囲気も聞きやすい。&lt;/li&gt;
&lt;li&gt;あ、これだ！&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;考えていること&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;話し手と聞き手がいて、二人が話すのをみんなで見る。&lt;/li&gt;
&lt;li&gt;話し手が、公開しているコンテンツ（blog、発表スライド、OSS、tweet 等々）をベースに話していく。&lt;/li&gt;
&lt;li&gt;話し手は、過去に公開したものがベースなので、スライド作るより準備が楽。当日も聞き手がいる方が話しやすい。&lt;/li&gt;
&lt;li&gt;場合によっては、コード書いて見せたりするのもあり。&lt;/li&gt;
&lt;li&gt;聞き手は、事前に質問を考えておく必要がある。負担を双方で受け持つ。&lt;/li&gt;
&lt;/ul&gt;


&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/shin1x1&quot;&gt;@shin1x1&lt;/a&gt; お願いした後、投げっぱなしではなくてお願いする側に負担があるのは良いですね！「質問事項は私の方で考えるので宜しくお願いします！！」って依頼しやすいです^^ あとディスカッション形式の方が本音聞けそう！（なんとなくですがｗ）&lt;/p&gt;&amp;mdash; ☯Takashi Hosoya（細谷崇） (@tkc49) &lt;a href=&quot;https://twitter.com/tkc49/status/810699630589538304&quot;&gt;December 19, 2016&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;h2&gt;期待していること&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;聞きたいことを聞きたい人に負担を少なめで聞ける。&lt;/li&gt;
&lt;li&gt;懇親会で話すような雰囲気でくだけた話をしたい。（「懇親会が本番」を本編で）&lt;/li&gt;
&lt;li&gt;参加している人も、二人が対話している方がリラックスして聞きやすいし、入りやすいのでは。&lt;/li&gt;
&lt;li&gt;間があってもいいし、二人いるから、どちらかが話せば良い。&lt;/li&gt;
&lt;li&gt;パネルディスカッションに比べると、各人に話を振っていく必要が無いから楽。&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;こんなセッションを 12/27 開催の関西 PHP 勉強会でやってみます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;第36回関西PHP勉強会 - Kansai PHP Users Group | Doorkeeper
&lt;a href=&quot;https://kphpug.doorkeeper.jp/events/55554&quot;&gt;https://kphpug.doorkeeper.jp/events/55554&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;どうせなら、録音して Podcast とかで出せるといいかなとかも考えてますが、予定は未定なので、ご都合が付く方は参加お待ちしています！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>Amazon Dash Button にみるドメイン特化クラスのヒント</title>
        <link href="https://blog.shin1x1.com/entry/domain-specific-class"/>
        <id>hatenablog://entry/10328749687197791519</id>
        <published>2016-12-07T15:00:00+09:00</published>
        <updated>2016-12-07T15:17:48+09:00</updated>        <summary type="html">この投稿は、PHP Advent Calendar 2016 - Qiita の 7 日目です。 DDD のようなドメインを意識した開発手法でなくても、ドメインコンテキストで必要な操作のみを実装したクラスを作ってみましょう話です。 Amazon Dash Button Amazon Dash Button は、ボタンが一つだけあるデバイスです。このボタンを押すと、あらかじめ決められた商品の注文が Amazon に送信され、商品が配送されるというシンプルなものです。 このデバイスは、「商品を届けて欲しい」というユースケースに対して、ボタンを押すというシンプルなインターフェイスを実装しています。単…</summary>
        <content type="html">&lt;p&gt;この投稿は、&lt;a href=&quot;http://qiita.com/advent-calendar/2016/php&quot;&gt;PHP Advent Calendar 2016 - Qiita&lt;/a&gt; の 7 日目です。&lt;/p&gt;

&lt;p&gt;DDD のようなドメインを意識した開発手法でなくても、ドメインコンテキストで必要な操作のみを実装したクラスを作ってみましょう話です。&lt;/p&gt;

&lt;h2&gt;Amazon Dash Button&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.jp/b?ie=UTF8&amp;amp;node=4752863051&quot;&gt;Amazon Dash Button&lt;/a&gt; は、ボタンが一つだけあるデバイスです。このボタンを押すと、あらかじめ決められた商品の注文が Amazon に送信され、商品が配送されるというシンプルなものです。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/B01L2WOYT2/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/514QIkvDWVL.jpg&quot; alt=&quot;サントリー天然水 Dash Button&quot; title=&quot;サントリー天然水 Dash Button&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このデバイスは、「商品を届けて欲しい」というユースケースに対して、ボタンを押すというシンプルなインターフェイスを実装しています。単にボタンを押すだけなので、その裏側でどのようにして実現されているかを知らずとも、誰もが利用できます。違う言い方をすれば、ボタンを押す（商品を注文する）ことしかできないので、操作方法を覚える必要もなく、想定外の使い方もやりようがありません。&lt;a href=&quot;#f-b57ea4fe&quot; name=&quot;fn-b57ea4fe&quot; title=&quot;誤って押して、想定外の注文が飛ぶことはありますが&quot;&gt;*1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このように、ドメインやそのユースケース、関心事に特化したインターフェイスを用意すれば、そのコンテキストの中では迷うことなく、自然に理解でき、扱い方を間違えることもありません。&lt;/p&gt;

&lt;h2&gt;ドメイン特化クラス&lt;/h2&gt;

&lt;p&gt;「ユーザに現在の年齢を尋ねる」というユースケースの実装を考えてみます。&lt;/p&gt;

&lt;p&gt;User クラスとして実装したのが、下記です。&lt;/p&gt;

&lt;p&gt;現在の年齢を算出するには、生年月日が必要となるので、コンストラクタで渡しています。生年月日は、&lt;code&gt;cakephp/chronos&lt;/code&gt; パッケージの Date クラスで表現しています。&lt;/p&gt;

&lt;p&gt;年齢を尋ねるには、age メソッドを利用します。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;
&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Generic;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Cake\Chronos\&lt;span class=&quot;synIdentifier&quot;&gt;Date&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; User
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;Date&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;Date $dateOfBirth&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;dateOfBirth &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; age&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;dateOfBirth&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;age;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;このコードを実行するのが、下記です。生年月日をコンストラクタを渡して、age メソッドを呼ぶだけなのでシンプルです。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;// 現在日を設定&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;// Chronos::setTestNow(Chronos::create(2016, 12, 8, 12, 34, 56));&lt;/span&gt;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;new&lt;/span&gt; User&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;::&lt;/span&gt;create&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;2000&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
&lt;span class=&quot;synPreProc&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;age&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;, PHP_EOL; &lt;span class=&quot;synComment&quot;&gt;// 16&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;このままでも動作は問題は無いのですが、気になるのは、Date クラスです。これは、日付を扱うクラスなので、挙動としては問題ありません。ですが、このクラスは汎用クラスなので、ドメインのコンテキストで扱う生年月日ではありません。プロパティ名を見れば、生年月日であることは想像できますが、より明示するために生年月日を示すクラスを実装します。&lt;/p&gt;

&lt;p&gt;生年月日を示したのが、下記の DateOfBirth クラスです。内部表現としては、Date クラスを利用しているので、ただのラッパーのようにも見えます。ここで大事なのは、Date クラスのインスタンスを内包して、DateOfBirth クラスでは、年齢を算出する age メソッドのみを実装しているという点です。これにより、DateOfBirth クラスが担うのは、年齢を算出するだけということが分かります。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Domain;

&lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; Cake\Chronos\&lt;span class=&quot;synIdentifier&quot;&gt;Date&lt;/span&gt;;

&lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; DateOfBirth
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;Date&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;date&lt;/span&gt;;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;Date $date&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;date &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;date&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; age&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;date&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;age;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;DateOfBirth クラスを使った User クラスは下記です。コンストラクタの型宣言が変わっているだけなのですが、Date クラスに比べると生年月日であることが読み取れます。もちろん、User クラス内で生年月日を操作する際も age メソッドしかできないので、間違いようがありません。&lt;/p&gt;

&lt;p&gt;もし、配送日などの別の日付クラスが存在したとしても、誤って User クラスに与えることができません。要求されている仕様が分かりやすいだけでなく、誤った引数に対するガードとしても有効です。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synType&quot;&gt;namespace&lt;/span&gt; Acme\Domain;

&lt;span class=&quot;synType&quot;&gt;class&lt;/span&gt; User
&lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@var &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;DateOfBirth&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@param &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;DateOfBirth $dateOfBirth&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;DateOfBirth &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;dateOfBirth &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;dateOfBirth&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;synComment&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;synPreProc&quot;&gt;@return &lt;/span&gt;&lt;span class=&quot;synComment&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;synComment&quot;&gt;     */&lt;/span&gt;
    &lt;span class=&quot;synType&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; age&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;synType&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;dateOfBirth&lt;span class=&quot;synType&quot;&gt;-&amp;gt;&lt;/span&gt;age&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;h2&gt;ドメイン特化クラス実装のポイント&lt;/h2&gt;

&lt;p&gt;このようなドメインに特化したクラスを実装するには色々な方法がありますが、いくつかポイントを書きます。&lt;/p&gt;

&lt;h3&gt;操作を必要最低限に絞る&lt;/h3&gt;

&lt;p&gt;PHP には、数多くの組み込みクラスやライブラリ、フレームワークがあります。一般的なデータ型を示すためのクラスは探せば見つかると思うので、多くの場合、それをベースとして利用するでしょう。&lt;/p&gt;

&lt;p&gt;その場合、継承ではなく上記の DateOfBirth クラスのように委譲を使って実装します。&lt;/p&gt;

&lt;p&gt;例えば、DateOfBirth クラスを Date クラスを継承して実装したとします。この時、DateOfBirth クラスは、Date クラスの public / protected メソッドを受け継ぐので、Date クラスと同様の操作が可能です。Date クラスには、日付の加算、減算といったメソッドがありますが、このコンテキストの生年月日に関してはそういった操作は不要です。不要なだけでなく、うっかり使ってしまうと想定外の動作を招く場合もあります。&lt;/p&gt;

&lt;p&gt;こうした違いは、IDE で開発すると明確です。委譲版 DateOfBirth は、age メソッドのみ補完候補となります。一方、継承版では、age メソッドに加えて、Date クラスのメソッドが多数出現します。一つしかボタンが無いのと、たくさんのボタンがあるのと、どちらが利用者として分かりやすいかは一目瞭然です。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;委譲版 DateOfBirth&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161207/20161207135012.png&quot; alt=&quot;f:id:shin1x1:20161207135012p:plain&quot; title=&quot;f:id:shin1x1:20161207135012p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;継承版 DateOfBirth&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161207/20161207135019.png&quot; alt=&quot;f:id:shin1x1:20161207135019p:plain&quot; title=&quot;f:id:shin1x1:20161207135019p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h3&gt;対象に特化する（汎用性を求めない）&lt;/h3&gt;

&lt;p&gt;上記、継承が使われる理由として、DRY を意識しすぎるため、継承で実装の再利用を行いたいというものがあります。&lt;/p&gt;

&lt;p&gt;例えば、このシステムに配送日といったモデルがあったとしましょう。生年月日も配送日も日付を示すものなので、Date クラスを基底クラスとし、それぞれ継承したとします。しかし、生年月日と配送日を同種のものとして扱う場面というのは、現実世界ではあまり思い当たりません。&lt;/p&gt;

&lt;p&gt;ドメインに特化したクラスの実装では、汎用的に実装する必要はなく、対象にのみフォーカスして実装します。&lt;/p&gt;

&lt;p&gt;これは、一般的な汎用クラスとは指向している方向が異なります。あくまでドメインに特化したクラスなので、汎用性を考える必要はありません。&lt;a href=&quot;#f-c160b107&quot; name=&quot;fn-c160b107&quot; title=&quot;ドメインコンテキスト内で汎用性を持たせるということは有り得ます。&quot;&gt;*2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;もし、実装を共有したいという目的であれば、共通処理を行う別クラスを実装して、そこに委譲するか、トレイトにまとめるという手法を取ると良いでしょう。&lt;/p&gt;

&lt;h3&gt;一つの概念を表す小さなクラスを作る&lt;/h3&gt;

&lt;p&gt;今回の生年月日のように、一つの概念のみを示すクラスを作るようにします。凝集度が高い小さなクラスを作ることで、実装しやすく、使いやすく、テストしやすいクラスになります。&lt;/p&gt;

&lt;p&gt;こうしたクラスは取り回しが楽なので、どのようなアーキテクチャであっても、取り入れることができます。特に、DDD のようなドメインを意識したアーキテクチャでなくても、一部だけでも、一つの概念だけでも、簡単に取り込むことができます。&lt;/p&gt;

&lt;p&gt;実際の開発では、ValueObject（値オブジェクト）として実装するところから始めると良いです。&lt;/p&gt;

&lt;h3&gt;コンテキストによって異なる&lt;/h3&gt;

&lt;p&gt;DDD でも「境界付けられたコンテキスト」があるように、コンテキストというのは重要な概念です。&lt;/p&gt;

&lt;p&gt;同じ「生年月日」でも、本エントリで扱う「生年月日」と別のシステムで扱う「生年月日」では、あるべき振る舞いが異なるかもしれません。今回は年齢の算出のみを実装しましたが、「今日が誕生日かどうか」「今月が誕生月か」といった振る舞いが必要となる場合もあるでしょう。むしろ、この2つのみが関心事であれば、「年齢を算出する」は不要かもしれません。&lt;/p&gt;

&lt;p&gt;このように、同じ用語、概念であっても、コンテキストによって求められるものは異なります。つまり、生年月日とはこうあるべきといった汎用の生年月日クラスを実装したとしても、それがそのまま適用できるかどうかは、コンテキストによって異なるということです。&lt;/p&gt;

&lt;h2&gt;ユースケース&lt;/h2&gt;

&lt;p&gt;こうしたドメインに特化したクラスを少しづつ導入したい場合は、ValueObject として導入するのが実装するのが良いでしょう。ValueObject にすれば、操作が限定できるだけでなく、オブジェクトの不変条件を内包したり、内部表現をカプセル化することができます。&lt;/p&gt;

&lt;p&gt;例えば、下記のようなものが考えられます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;日付（日付ライブラリは高機能なわり、ドメインで要求されるものは一部。）&lt;/li&gt;
&lt;li&gt;数値、特に演算が行われるもの（金額、ポイントなど。演算の限定、制約条件の付与。）&lt;/li&gt;
&lt;li&gt;要求仕様が明確なデータ（商品コードなど。）&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;それ以外でも、サービスのように状態を保持しない処理もドメイン特化クラスで実装しやすい箇所です。&lt;/p&gt;

&lt;p&gt;例えば、暗号処理なら、実際の暗号化や複合処理は別クラスで実装し、後はユースケース別のクラスからこれらのクラスを委譲で利用して、暗号化や複合を行うという場合に使えます。暗号処理は、暗号化と複合をペアで行う場合が多いので、ベアごとにクラスを分けると、ここで処理で暗号化したものは、あそこで複合しているというのが分かりやすくなります。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;こうしたドメインに特化したクラスを作るというのは、一見面倒なように見えますが、やってみると面白いものです。&lt;/p&gt;

&lt;p&gt;生年月日も、汎用日付オブジェクトで表現すれば、それで終わりです。一方、生年月日クラスを実装すると、生年月日にどのような振る舞いが求められているかを考えることになります。このような視点で仕様を見ていけば、一つ一つの用語にも関心がいき、より深くドメインを理解して、コードに表現するようになります。&lt;/p&gt;

&lt;p&gt;これは、教条的なものではなく、むしろ感覚に近いもので、こうしたプラクティスを重ねていくことで、身についていくもののように思います。&lt;/p&gt;

&lt;p&gt;ドメインに必要な部品を少しづつ作っていき、それを組み合わせて構築していくという作業は、安心感もあり、とても楽しい作業です。身近なところから、取り組んでみてはどうでしょうか。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;http://blog.shin1x1.com/embed/ddd-with-laravel&quot; title=&quot;「DDD パターンを活用した Laravel アプリケーション開発」を Laravel Osaka 2016 で発表しました。 - Shin x Blog&quot; class=&quot;embed-card embed-blogcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://blog.shin1x1.com/entry/ddd-with-laravel&quot;&gt;blog.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-b57ea4fe&quot; name=&quot;f-b57ea4fe&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;誤って押して、想定外の注文が飛ぶことはありますが&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-c160b107&quot; name=&quot;f-c160b107&quot; class=&quot;footnote-number&quot;&gt;*2&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;ドメインコンテキスト内で汎用性を持たせるということは有り得ます。&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="http://ecx.images-amazon.com/images/I/514QIkvDWVL.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「DDD パターンを活用した Laravel アプリケーション開発」を Laravel Osaka 2016 で発表しました。</title>
        <link href="https://blog.shin1x1.com/entry/ddd-with-laravel"/>
        <id>hatenablog://entry/10328749687193886820</id>
        <published>2016-11-15T11:00:00+09:00</published>
        <updated>2016-11-15T11:00:25+09:00</updated>        <summary type="html">2016/10/19 に大阪で開催された Laravel Osaka 2016 にて、「DDD パターンを活用した Laravel アプリケーション開発」を発表しました。 会場の MOTEX さん。巨大スクリーンが 2 面あり、話しやすい環境でした。 発表資料 Laravel の具体的なテクニックに比べると抽象的な内容なので、どれだけ伝えられるか思案したのですが、聞いて頂いた方からのフィードバックや参加者アンケートでも概ね良い評価を頂けたので安心しました。 ValueObject については、さらに掘り下げて話せるテーマなので、これ単体でもまた話してみたいです。 Value Object は基…</summary>
        <content type="html">&lt;p&gt;2016/10/19 に大阪で開催された &lt;a href=&quot;http://php-jp.github.io/laravel-osaka-2016/&quot;&gt;Laravel Osaka 2016&lt;/a&gt; にて、「DDD パターンを活用した Laravel アプリケーション開発」を発表しました。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161110/20161110215814.jpg&quot; alt=&quot;f:id:shin1x1:20161110215814j:plain&quot; title=&quot;f:id:shin1x1:20161110215814j:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;会場の MOTEX さん。巨大スクリーンが 2 面あり、話しやすい環境でした。&lt;/p&gt;

&lt;h2&gt;発表資料&lt;/h2&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;f59a77c417fc4f95a4292b41df1bb640&quot; data-ratio=&quot;1.33333333333333&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;Laravel の具体的なテクニックに比べると抽象的な内容なので、どれだけ伝えられるか思案したのですが、聞いて頂いた方からのフィードバックや参加者アンケートでも概ね良い評価を頂けたので安心しました。&lt;/p&gt;

&lt;p&gt;ValueObject については、さらに掘り下げて話せるテーマなので、これ単体でもまた話してみたいです。&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;Value Object は基本ですね | &lt;br&gt;DDDパターンを活用した Laravelアプリケーション開発/ddd-with-laravel &lt;a href=&quot;https://t.co/ZzRTnt0tY6&quot;&gt;https://t.co/ZzRTnt0tY6&lt;/a&gt;&lt;/p&gt;&amp;mdash; 増田 亨． (@masuda220) &lt;a href=&quot;https://twitter.com/masuda220/status/792323693003218944&quot;&gt;October 29, 2016&lt;/a&gt;&lt;/blockquote&gt;


&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;


&lt;p&gt;いつも blog や資料を参考にさせて頂いている増田さんに tweet して頂きました。インターネットっていいですね :)&lt;/p&gt;

&lt;h2&gt;質疑応答&lt;/h2&gt;

&lt;p&gt;発表の後の質疑応答では、多数の質問がありました。質問に対して的確な答えができたかは別にして、その場で今話した内容について意見交換ができるのは楽しいですね。&lt;/p&gt;

&lt;p&gt;さらに、イベント終了後の懇親会や、後日、別の場でも発表についての質問や意見があり、そこから議論に発展することもありました。&lt;/p&gt;

&lt;p&gt;話したことに対するリアクションが貰えるのは嬉しい限りで、発表者冥利に付きますね。&lt;/p&gt;

&lt;p&gt;質問頂いた内容は、ざっと下記のようなものでした。（発表後の質疑応答以外のものも含む）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DDD をはじめるには、どこから手を付ければ良いか？&lt;/li&gt;
&lt;li&gt;ValueObject を多数作った際のパフォーマンスについて&lt;/li&gt;
&lt;li&gt;デメテルの法則について&lt;/li&gt;
&lt;li&gt;なぜ、あえて Laravel を使っているのか？&lt;/li&gt;
&lt;li&gt;リポジトリパターンと Eloquent について&lt;/li&gt;
&lt;li&gt;遅延評価クエリをどう組み込むか&lt;/li&gt;
&lt;li&gt;例外の使い方&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;今回の発表内容は、Laravel に関する内容は一部のみで、大筋はどういったフレームワーク、言語でも関連することなので、色々な人が自分なりの解釈で考えやすい題材であったのが一つ理由としてあるでしょう。&lt;/p&gt;

&lt;p&gt;また、各技術の紹介や解説の発表に比べ、それらをどういった考え方で、いかに組み合わせるかというものは少ないように思います。&lt;/p&gt;

&lt;p&gt;システム開発を行うならば、誰もが日々頭を捻りながら行っている行為なのですが、こういった話が勉強会などで表に出ることはあまり多くありません。&lt;/p&gt;

&lt;p&gt;ドメインやコンテキストがそれぞれ異なるので、他人に話しにくいというのはありますが、今回のようにある程度抽象化すれば、みんなで考えられる題材になるので、もっと色々な人がこういった話をすると良いですね。（私はぜひ聞きたいです！）&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;Laravel Osaka 2016 は、おそらく国内での Laravel 単体のイベントとしては最も大きい規模だったと思います。
第 1 回目ということで、関西だけでなく、東京や福岡からスピーカーも参加者も集まっていて、どのセッションも興味深い内容でした。&lt;/p&gt;

&lt;p&gt;スタッフのみなさん、楽しいイベントをありがとうございました！&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161111/20161111113629.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>「正規表現再入門」を PHP カンファレンス 2016 で発表してきました</title>
        <link href="https://blog.shin1x1.com/entry/re-introduction-to-regex"/>
        <id>hatenablog://entry/10328749687192898731</id>
        <published>2016-11-04T10:43:21+09:00</published>
        <updated>2016-11-05T16:21:51+09:00</updated>        <summary type="html">2016/11/03 に開催された PHP カンファレンス 2016 にて、「正規表現再入門」を発表してきました。 資料 speakerdeck.com togetter.com 内容は、正規表現のマッチングの動き、量指定子のマッチングパターン、バックトラックやパフォーマンスへの影響についてです。 下記のエントリを下地にして、マッチングの動きを分かりやすく伝えることを意識してお話しました。 blog.shin1x1.com 参加された方からは、「分かりやすかった」といった好意的なフィードバックを頂けたので、発表して良かったです。 これから正規表現を学ぶにしても、マッチングがどのように行われるか…</summary>
        <content type="html">&lt;p&gt;2016/11/03 に開催された PHP カンファレンス 2016 にて、「正規表現再入門」を発表してきました。&lt;/p&gt;

&lt;h2&gt;資料&lt;/h2&gt;

&lt;p&gt;&lt;iframe allowfullscreen=&quot;true&quot; allowtransparency=&quot;true&quot; frameborder=&quot;0&quot; height=&quot;596&quot; id=&quot;talk_frame_366488&quot; mozallowfullscreen=&quot;true&quot; src=&quot;//speakerdeck.com/player/19671b53c88d432080493e774dcc16a2&quot; style=&quot;border:0; padding:0; margin:0; background:transparent;&quot; webkitallowfullscreen=&quot;true&quot; width=&quot;710&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://speakerdeck.com/shin1x1/introduction-to-regex&quot;&gt;speakerdeck.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=http%3A%2F%2Ftogetter.com%2Fli%2F1044243&quot; title=&quot;これを読んであなたも正規表現マスター！正規表現再入門 #phpcon2016 #phpcon2016_2&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://togetter.com/li/1044243&quot;&gt;togetter.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;内容は、正規表現のマッチングの動き、量指定子のマッチングパターン、バックトラックやパフォーマンスへの影響についてです。&lt;/p&gt;

&lt;p&gt;下記のエントリを下地にして、マッチングの動きを分かりやすく伝えることを意識してお話しました。
&lt;iframe src=&quot;http://blog.shin1x1.com/embed/regex-performance&quot; title=&quot;パフォーマンスを意識して正規表現を書く - Shin x Blog&quot; class=&quot;embed-card embed-blogcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;http://blog.shin1x1.com/entry/regex-performance&quot;&gt;blog.shin1x1.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;参加された方からは、「分かりやすかった」といった好意的なフィードバックを頂けたので、発表して良かったです。&lt;/p&gt;

&lt;p&gt;これから正規表現を学ぶにしても、マッチングがどのように行われるかをざっくりと知っておけば、正規表現を書く際にも動きが想像できるので、この発表がお役に立てると嬉しいです。&lt;/p&gt;

&lt;p&gt;偶然、同じ時間帯に発表があった徳丸さんのセッションでも正規表現に触れられていたようで、Twitter の TL が微妙に交錯しているのが面白かったです :)&lt;/p&gt;

&lt;h2&gt;SQL と似た感覚&lt;/h2&gt;

&lt;p&gt;下地となったエントリでも書いたのですが、正規表現と SQL はやはり似ているなあと感じます。
懇親会でも、発表を聞いて頂いた方との会話で話題になりました。&lt;/p&gt;

&lt;p&gt;宣言的に書く言語は、記述しやすく理解しやすいのですが、どのように実行されるかはブラックボックスになります。
もちろん、それは利点（How ではなく、What に集中できる）なので、実行エンジンが良しなにやってくれる時は全く持って問題ありません。&lt;/p&gt;

&lt;p&gt;ただ、今回のバックトラック爆発のように仕組み上発生しうる挙動に関しては、利用側がそれを意識して書く必要があります。&lt;/p&gt;

&lt;p&gt;特に、正規表現はバリデーションで外部から来た値をそのまま扱う場面でよく利用されるため、問題があればすぐに影響を受けることになります。&lt;/p&gt;

&lt;p&gt;SQL を書く時にデータベースエンジンの動きをざっくりでも意識するように、正規表現でも意識すると良いですね。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;今年は、午後最後のセッションで、途中にサイン会などもあったので、t_wada さんのセッションだけ参加しました。&lt;/p&gt;

&lt;p&gt;このセッションは素晴らしくて、同じように参加した人からも大絶賛でした。
内容はもちろんのこと、構成や説明など伝え方がとても参考になりました。自分の発表にも取り入れられる要素があったので、今後に生かしていきたいです。&lt;/p&gt;

&lt;p&gt;懇親会やその後の N 次会でも、色々な方とお話ができて、楽しい時間でした。毎年そうですが、蒲田の PHP カンファレンスは最高ですね！&lt;/p&gt;

&lt;p&gt;運営のみなさん、今年も楽しいイベントをありがとうございました。&lt;/p&gt;
</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20161105/20161105162036.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>パフォーマンスを意識して正規表現を書く</title>
        <link href="https://blog.shin1x1.com/entry/regex-performance"/>
        <id>hatenablog://entry/10328749687179537998</id>
        <published>2016-08-23T11:00:00+09:00</published>
        <updated>2016-08-23T11:00:07+09:00</updated>        <summary type="html">正規表現を書く際、どのようなパターンにマッチさせるか、どこをキャプチャするかという視点で記述することはあっても、パフォーマンスを考えて記述するというのはある程度知っている人でなければ忘れがちな視点です。 このエントリでは、バックトラックをメインに正規表現がパフォーマンスに及ぼす挙動について見ていきます。 対象の正規表現エンジン ここでは、従来型 NFA を対象としています。具体的には、PHP の preg_ 関数で利用している PCRE や mb_ereg 関数が利用している鬼車です。Perl や Ruby、Python、Java、.NET でも従来型 NFA を採用しているので、似た挙動とな…</summary>
        <content type="html">&lt;p&gt;正規表現を書く際、どのようなパターンにマッチさせるか、どこをキャプチャするかという視点で記述することはあっても、パフォーマンスを考えて記述するというのはある程度知っている人でなければ忘れがちな視点です。&lt;/p&gt;

&lt;p&gt;このエントリでは、バックトラックをメインに正規表現がパフォーマンスに及ぼす挙動について見ていきます。&lt;/p&gt;

&lt;h2&gt;対象の正規表現エンジン&lt;/h2&gt;

&lt;p&gt;ここでは、従来型 NFA を対象としています。具体的には、PHP の preg_&lt;em&gt; 関数で利用している PCRE や mb_ereg&lt;/em&gt; 関数が利用している鬼車です。Perl や Ruby、Python、Java、.NET でも従来型 NFA を採用しているので、似た挙動となるでしょう。&lt;/p&gt;

&lt;p&gt;「従来型 NFA」や「バックトラック」などの用語については、「&lt;a href=&quot;http://d.hatena.ne.jp/asin/4873113598/shinxblog-22&quot;&gt;詳説 正規表現 第3版&lt;/a&gt;」のものを用いています。&lt;/p&gt;

&lt;h2&gt;バックトラックによるマッチ探査&lt;/h2&gt;

&lt;p&gt;正規表現エンジンでは、指定された文字列が、パターンにマッチするかどうかを判別する際、記述された正規表現で取り得るマッチパターンが見つかるように何度もマッチングを行います。&lt;/p&gt;

&lt;p&gt;例えば、&lt;code&gt;\d+\d&lt;/code&gt; という正規表現があり、これに &lt;code&gt;123&lt;/code&gt; という文字列がマッチするか preg_match 関数でチェックします。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synIdentifier&quot;&gt;var_dump&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;/\d+\d/&lt;/span&gt;&#39;, &#39;&lt;span class=&quot;synConstant&quot;&gt;123&lt;/span&gt;&#39;&lt;span class=&quot;synSpecial&quot;&gt;))&lt;/span&gt;;
&lt;/pre&gt;


&lt;p&gt;これを実行すると、&lt;code&gt;\d+&lt;/code&gt; の部分は、文字列 &lt;code&gt;123&lt;/code&gt; にマッチします &lt;a href=&quot;#f-b853dabb&quot; name=&quot;fn-b853dabb&quot; title=&quot;これは、PCRE の量指定子が最大量指定子なためです。最小量指定子や絶対最大指定子を指定したり、正規表現エンジンタイプが異なればマッチの仕方は変わります。&quot;&gt;*1&lt;/a&gt;。しかし、次の &lt;code&gt;\d&lt;/code&gt; にマッチする文字列が無いので、一つ前のパターン（&lt;code&gt;\d+&lt;/code&gt;）に戻ります。次は、&lt;code&gt;\d+&lt;/code&gt; を &lt;code&gt;12&lt;/code&gt; にマッチさせます。次の &lt;code&gt;\d&lt;/code&gt; は、&lt;code&gt;3&lt;/code&gt; にマッチするので、これで全体のマッチが成功します。&lt;/p&gt;

&lt;p&gt;下記では、この流れを示しています。&lt;code&gt;()&lt;/code&gt;が &lt;code&gt;\d+&lt;/code&gt; に、&lt;code&gt;{}&lt;/code&gt; が &lt;code&gt;\d&lt;/code&gt; がマッチした箇所で、&lt;code&gt;[]&lt;/code&gt;が現在マッチを試みている文字になります。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;* (123)[]  &amp;lt;--- `\d+`はマッチするが、`\d`がマッチしないので、`\d+`へ戻る
* (12){3}  &amp;lt;--- `\d+`も`\d`もマッチする&lt;/pre&gt;


&lt;p&gt;正規表現は、前から順に適用されていくのですが、後続のパターンがマッチしない場合に一つ前のパターンに戻って、別のマッチ方法を試行するのをバックトラック（backtracking）と呼びます。バックトラックは、正規表現に限らず、正しい解を探るためのアルゴリズムです。詳細は、下記で。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%82%AF%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0&quot;&gt;https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%82%AF%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;「マッチしない」を確定させるのは遠い道程&lt;/h2&gt;

&lt;p&gt;次は、マッチが失敗するパターンとして、正規表現 &lt;code&gt;\d+\d[^\d]&lt;/code&gt; を &lt;code&gt;123&lt;/code&gt; という文字列にマッチさせます。&lt;/p&gt;

&lt;p&gt;はじめに &lt;code&gt;\d+&lt;/code&gt; は文字列 &lt;code&gt;123&lt;/code&gt; にマッチします。次の &lt;code&gt;\d&lt;/code&gt; にマッチしない（文字列が無い）ので、バックトラックが発生します。次は、&lt;code&gt;\d+&lt;/code&gt;は文字列 &lt;code&gt;12&lt;/code&gt; に、&lt;code&gt;\d&lt;/code&gt; は文字列 &lt;code&gt;3&lt;/code&gt; にマッチします。最後の &lt;code&gt;[^\d]&lt;/code&gt; にマッチしない（文字列が無い）ので、バックトラックが発生します。今度は、&lt;code&gt;\d+&lt;/code&gt; を 文字列 &lt;code&gt;1&lt;/code&gt; に、&lt;code&gt;\d&lt;/code&gt; を 文字列 &lt;code&gt;2&lt;/code&gt; にマッチさせますが、&lt;code&gt;[^\d]&lt;/code&gt; と 文字列 &lt;code&gt;3&lt;/code&gt; はマッチしません。&lt;/p&gt;

&lt;p&gt;次は、文字列の先頭を 1 文字進めて &lt;code&gt;23&lt;/code&gt; に対してのマッチを試みますが、こちらもマッチには成功しません。さらに、文字列 &lt;code&gt;3&lt;/code&gt; に対するマッチも失敗し、最終的には全てのパターンで失敗します。&lt;/p&gt;

&lt;p&gt;下記では、この流れを示しています。&lt;code&gt;()&lt;/code&gt;が &lt;code&gt;\d+&lt;/code&gt; に、&lt;code&gt;{}&lt;/code&gt; が &lt;code&gt;\d&lt;/code&gt; がマッチした箇所で、&lt;code&gt;[]&lt;/code&gt;が現在マッチを試みている文字になります。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;* (123)[]    &amp;lt;--- `\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* (12){3}[]  &amp;lt;--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* (1){2}[3]  &amp;lt;--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* 1(23)[]    &amp;lt;--- 1文字進めてマッチング開始。`\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* 1(2){3}[]  &amp;lt;--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* 12(3)[]    &amp;lt;--- 1文字進めてマッチング開始。`\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* 123[]      &amp;lt;--- 1文字進めてマッチング開始。`\d+`がマッチせずに終了&lt;/pre&gt;


&lt;p&gt;このように、「マッチしない」と結論付けるために、指定した正規表現で取り得る全てのマッチングを行います。この挙動が場合によっては、パフォーマンスに大きな影響を及ぼします。&lt;/p&gt;

&lt;h2&gt;爆発するマッチング&lt;/h2&gt;

&lt;p&gt;マッチさせるまで（マッチしないことを確定させるため）、あらゆるマッチングを行うがゆえに正規表現や文字列によっては、マッチングの組み合わせパターンが膨大になる可能性があります。&lt;/p&gt;

&lt;p&gt;マッチングが膨大になる様を確認するために、regex101.com というサイトを利用します。このサイトでは、正規表現と文字列を指定すると、正規表現エンジンがどのようにマッチングを行っていくかを表示してくれます。&lt;/p&gt;

&lt;p&gt;&lt;iframe src=&quot;//hatenablog-parts.com/embed?url=https%3A%2F%2Fregex101.com%2F&quot; title=&quot;Online regex tester and debugger: JavaScript, Python, PHP, and PCRE&quot; class=&quot;embed-card embed-webcard&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; style=&quot;display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;&quot;&gt;&lt;/iframe&gt;&lt;cite class=&quot;hatena-citation&quot;&gt;&lt;a href=&quot;https://regex101.com/&quot;&gt;regex101.com&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;例えば、上記の例（&lt;code&gt;\d+\d[^\d]&lt;/code&gt;）であれば、下のような表示となります。これを見ると、文字列 &lt;code&gt;123&lt;/code&gt; に対して、14 steps がかかっていることが分かります。&lt;/p&gt;

&lt;p&gt;&lt;span itemscope itemtype=&quot;http://schema.org/Photograph&quot;&gt;&lt;img src=&quot;https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20160817/20160817160205.png&quot; alt=&quot;f:id:shin1x1:20160817160205p:plain&quot; title=&quot;f:id:shin1x1:20160817160205p:plain&quot; class=&quot;hatena-fotolife&quot; itemprop=&quot;image&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;ここで文字列を &lt;code&gt;123456789&lt;/code&gt; という 9 文字に増やすと、91 steps となります。&lt;/p&gt;

&lt;p&gt;マッチングが膨大になる場合を見るために、&lt;code&gt;(\d*\d+)+\d[^\d]&lt;/code&gt; という正規表現を利用します。これは、前述の &lt;code&gt;\d+\d[^\d]&lt;/code&gt; とマッチする文字列は同じです。このパターンを前述の 9 文字にマッチさせると、なんと 33,805 steps になりました！&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://regex101.com/r/eB0dM5/1&quot;&gt;https://regex101.com/r/eB0dM5/1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このようにわずか 9 文字の文字列に対するマッチングでも正規表現の書き方によって、マッチングの組み合わせが膨大に膨らむことが分かります。より複雑な正規表現や与えられる文字列が長い場合は、パフォーマンスへの影響が出てきます。&lt;/p&gt;

&lt;h2&gt;ReDosという攻撃&lt;/h2&gt;

&lt;p&gt;こうした正規表現エンジンの特性を利用した攻撃が ReDos です。ReDos については、下記の大垣さんのエントリが参考になります。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.ohgaki.net/avoiding-redos&quot;&gt;ReDoS&amp;#x306E;&amp;#x56DE;&amp;#x907F; | yohgaki&amp;#39;s blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;特に量指定子（&lt;code&gt;*&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt;、&lt;code&gt;+&lt;/code&gt;、&lt;code&gt;{n,m}&lt;/code&gt;）を入れ子にしたり、同じ文字クラスを重ねたりすると組み合わせパターンが多くなるので、こうした問題が起こる可能性があります。&lt;/p&gt;

&lt;h2&gt;末尾にあるスペースにマッチ&lt;/h2&gt;

&lt;p&gt;上記の ReDos とは別のパターンですが、単純な正規表現でも DoS を引き起こす場合があります。&lt;/p&gt;

&lt;p&gt;stackoverflow.com では、文字列前後のスペースを除去する &lt;code&gt;^[\s\u200c]+|[\s\u200c]+$&lt;/code&gt; という正規表現に対して 20,000 文字のスペースが含まれた文字列が送信されたために、上記のマッチングの組み合わせが膨大になり、34 分間アクセス不能になるという事態がありました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://stackstatus.net/post/147710624694/outage-postmortem-july-20-2016&quot;&gt;Stack Exchange Network Status &amp;mdash; Outage Postmortem - July 20, 2016&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.ohgaki.net/stackexchange-redos-attack&quot;&gt;StackExchange&amp;#x304C;&amp;#x653B;&amp;#x6483;&amp;#x3055;&amp;#x308C;&amp;#x305F;ReDoS&amp;#x306E;&amp;#x52B9;&amp;#x679C; | yohgaki&amp;#39;s blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;大垣さんのサイトでも実証されていますが、Stack Overflow の事例を参考に文字列末尾にあるスペースを正規表現でマッチさせて取り除くという処理を見てみます。&lt;/p&gt;

&lt;p&gt;まずは、正規表現を単純化して、&lt;code&gt;\s+$&lt;/code&gt; を利用し、文字列は &lt;code&gt;  a&lt;/code&gt;（半角スペース 3 文字）にマッチさせる場合で動きを見ます。&lt;/p&gt;

&lt;p&gt;この場合、下記のようにマッチングを行なわれます。半角スペースが見えるように &lt;code&gt;_&lt;/code&gt; としています。人間が見れば、文字列の末尾が &lt;code&gt;a&lt;/code&gt; なので、どのようにマッチングさせても成功しないのは明白なのですが、正規表現エンジンでは、前から順に愚直に評価していきます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;* (___)[a] &amp;lt;--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* (__)[_]a &amp;lt;--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* (_)[_]_a &amp;lt;--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* _(__)[a] &amp;lt;--- 1文字進めてマッチング。`\s+`はマッチするが、`$`がマッチしないのでバックトラック
* _(_)[_]a &amp;lt;--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* __(_)a &amp;lt;--- 1文字進めてマッチング。`\s+`はマッチするが、`$`がマッチしないのでバックトラック
* ___[a] &amp;lt;--- 1文字進めてマッチング。`\s+`がマッチしないのでバックトラック
* ___a[] &amp;lt;--- 1文字進めてマッチング。`\s+`がマッチせずに終了&lt;/pre&gt;


&lt;p&gt;スペースが 3 文字ではなく、20,000 文字となれば、それだけ多くのバックトラックが発生するため、処理に時間がかかるようになります。&lt;/p&gt;

&lt;p&gt;パフォーマンスを計測するために下記のコードを実行してみました。それぞれのスペースの最後には &lt;code&gt;a&lt;/code&gt; が付いているのでマッチングは失敗します。&lt;/p&gt;

&lt;pre class=&quot;code lang-php&quot; data-lang=&quot;php&quot; data-unlink&gt;&lt;span class=&quot;synSpecial&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; benchmark&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;title&lt;/span&gt;, callable &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synPreProc&quot;&gt;echo&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;# &lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;title&lt;/span&gt;, PHP_EOL;
    &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;microtime&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt;;
    &lt;span class=&quot;synPreProc&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;microtime&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;start&lt;/span&gt;, PHP_EOL;
    &lt;span class=&quot;synPreProc&quot;&gt;echo&lt;/span&gt; PHP_EOL;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;[]&lt;/span&gt;;
&lt;span class=&quot;synStatement&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;synConstant&quot;&gt;20&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;200&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;2000&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;20000&lt;/span&gt;, &lt;span class=&quot;synConstant&quot;&gt;200000&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;str_repeat&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt; &lt;/span&gt;&#39;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &#39;&lt;span class=&quot;synConstant&quot;&gt;a&lt;/span&gt;&#39;;
    benchmark&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;preg_replace&lt;/span&gt;&#39; &lt;span class=&quot;synStatement&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;no&lt;/span&gt;, &lt;span class=&quot;synPreProc&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;synPreProc&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;synSpecial&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;synStatement&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;synIdentifier&quot;&gt;preg_replace&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;(&lt;/span&gt;&#39;&lt;span class=&quot;synConstant&quot;&gt;/\s+$/&lt;/span&gt;&#39;, &#39;&#39;, &lt;span class=&quot;synStatement&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;synIdentifier&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;synSpecial&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;synSpecial&quot;&gt;})&lt;/span&gt;;
&lt;span class=&quot;synSpecial&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;このコードを PHP 5.6 と 7.0 で実行した結果が以下です。2,000文字あたりから処理速度が低下していき、20,000文字、200,000文字で急激に処理時間がかかっています。なお、5.6 より 7.0 の方が文字数が多い場合は処理が速いので、何かしらの最適化が行われているのかもしれません。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 5.6.24&lt;/li&gt;
&lt;/ul&gt;


&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文字長&lt;/th&gt;
&lt;th style=&quot;text-align:right;&quot;&gt;実行時間&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.05ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.35ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;7.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;2,438ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;233,513ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;ul&gt;
&lt;li&gt;PHP 7.0.9&lt;/li&gt;
&lt;/ul&gt;


&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文字長&lt;/th&gt;
&lt;th style=&quot;text-align:right;&quot;&gt;実行時間&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.4ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;5.8ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;498ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;50,352ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;h2&gt;正規表現を改良&lt;/h2&gt;

&lt;p&gt;文字列末尾のスペースにマッチングさせる正規表現を改善するために &lt;code&gt;\s++$&lt;/code&gt; に変えてみます。元の最大量指定子（&lt;code&gt;\s+&lt;/code&gt;）を絶対最大量指定子（&lt;code&gt;\s++&lt;/code&gt;）に変更しただけです。&lt;/p&gt;

&lt;p&gt;これでベンチマークを取ると下記のようになりました。両バージョンとも処理速度が改善しており、200,000 文字で見ると、PHP 5.6 で 18 倍、PHP 7.0 で 4 倍と大きな改善となっています。変更後は、PHP 5.6 と PHP 7.0 でほぼ互角になっているのも興味深い点です。&lt;/p&gt;

&lt;p&gt;このように正規表現の書き方一つで、大きくパフォーマンスが変わる場合があります。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 5.6.24&lt;/li&gt;
&lt;/ul&gt;


&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文字長&lt;/th&gt;
&lt;th style=&quot;text-align:right;&quot;&gt;実行時間&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.17ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.041ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;1.91ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;172ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;12,879ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;ul&gt;
&lt;li&gt;PHP 7.0.9&lt;/li&gt;
&lt;/ul&gt;


&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文字長&lt;/th&gt;
&lt;th style=&quot;text-align:right;&quot;&gt;実行時間&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.26ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;0.047ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;1.29ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;123ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200,000文字&lt;/td&gt;
&lt;td style=&quot;text-align:right;&quot;&gt;12,544ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;絶対最大量指定子を使った場合（&lt;code&gt;  a&lt;/code&gt;へのマッチング）は、下記のようなマッチングを行います。絶対最大量指定子は、貪欲なマッチングとなり、かつマッチした文字列をアトミックに扱います。これにより、バックトラックの発生が抑えられるのでマッチング回数を抑制できます。これは、アトミックグループ（&lt;code&gt;(?&amp;gt;)&lt;/code&gt;）でも同様の効果が得られます。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;* (___)[a] &amp;lt;--- `\s++`はマッチするが、`$`がマッチしないのでバックトラック
* _(__)[a] &amp;lt;--- 1文字進めてマッチング。`\s++`はマッチするが、`$`がマッチしないのでバックトラック
* __(_)a   &amp;lt;--- 1文字進めてマッチング。`\s++`はマッチするが、`$`がマッチしないのでバックトラック
* ___[a]   &amp;lt;--- 1文字進めてマッチング。`\s++`がマッチしないのでバックトラック
* ___a[]   &amp;lt;--- 1文字進めてマッチング。`\s++`がマッチせずに終了&lt;/pre&gt;


&lt;p&gt;今回計測したPHPコードは、下記です。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/shin1x1/99b3ca3f8ba5799e29537b6ce4b37e65#file-regex_trim-php&quot;&gt;regex_trim.php &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;あえて正規表現を避ける&lt;/h2&gt;

&lt;p&gt;PHP でこの処理を単純に考えれば、ltrim関数で簡単に実装できます。&lt;/p&gt;

&lt;p&gt;ltrim関数を使うと、200,000文字のスペース + &lt;code&gt;a&lt;/code&gt; の場合でも、わずか 1ms 以下でした。&lt;/p&gt;

&lt;p&gt;stackoverflow.com では、正規表現を使うことをやめ、文字列処理（substring関数）に取り替えたようです。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;正規表現の書き方や文字列が、パフォーマンスに影響を与えることを見てきました。&lt;/p&gt;

&lt;p&gt;マッチングパターンのみに注力して記述した正規表現が、正規表現エンジンによって、上手い具合にチューニングされて動作すれば良いのですが、最適化が上手く働かないケースでは、その挙動を把握した対処を行わないとパフォーマンスの問題を抱えることになります。&lt;/p&gt;

&lt;p&gt;このあたりは、SQL にも似たものを感じしますね。&lt;/p&gt;

&lt;p&gt;正規表現は、Web アプリケーションにおいてはバリデーションで利用されることが多く、外部の値をいきなり正規表現にかけてチェックするということもあります。バリデーションは成功しないが、DoS となるような値を投げられて、パフォーマンスが低下したり、サービスが停止させられる場合も考えられます。&lt;/p&gt;

&lt;p&gt;正規表現のパターンを考慮する、文字列長をチェックしておく、場合によっては正規表現を使わない、など対処法は考えられるが、まずは正規表現がパフォーマンス上の問題になりうるということを認識することが第一歩になるでしょう。&lt;/p&gt;

&lt;h2&gt;参考&lt;/h2&gt;

&lt;p&gt;正規表現エンジンの挙動については、下記の 2 冊がとても参考になります。どちらかというと、「詳説 正規表現」は正規表現を利用する側、「正規表現技術入門」は正規表現エンジンを実装する側から正規表現を見た本となっています。どちらも内部の動きを分かりやすく解説しているので、本エントリのような内容をより知りたい方には特におすすめです。&lt;/p&gt;

&lt;p&gt;どちらも Kindle 版が無いので、電子版を購入する場合は、オライリー社サイト、技術評論社サイトで購入します。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4873113598/shinxblog-22/&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/51cRxtwo7IL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;詳説 正規表現 第3版&quot; title=&quot;詳説 正規表現 第3版&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4873113598/shinxblog-22/&quot;&gt;詳説 正規表現 第3版&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; Jeffrey E.F. Friedl,株式会社ロングテール,長尾高弘&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; オライリージャパン&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2008/04/26&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 大型本&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;購入&lt;/span&gt;: 24人 &lt;span class=&quot;hatena-asin-detail-label&quot;&gt;クリック&lt;/span&gt;: 754回&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4873113598/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (85件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4774172707/shinxblog-22/&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/51WdZhhrLYL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)&quot; title=&quot;正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/4774172707/shinxblog-22/&quot;&gt;正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; 新屋良磨,鈴木勇介,高田謙&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 技術評論社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2015/04/14&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 単行本（ソフトカバー）&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4774172707/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (1件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-b853dabb&quot; name=&quot;f-b853dabb&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;これは、PCRE の量指定子が最大量指定子なためです。最小量指定子や絶対最大指定子を指定したり、正規表現エンジンタイプが異なればマッチの仕方は変わります。&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="https://cdn-ak.f.st-hatena.com/images/fotolife/s/shin1x1/20160817/20160817160205.png" type="image/png" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
    
    
    <entry>
        <title>より実践的なDDD本「.NETのエンタープライズアプリケーションアーキテクチャ第2版 」</title>
        <link href="https://blog.shin1x1.com/entry/naa4e"/>
        <id>hatenablog://entry/10328749687175318852</id>
        <published>2016-07-26T11:00:00+09:00</published>
        <updated>2016-07-26T11:00:02+09:00</updated>        <summary type="html">DDD 関連の書籍といえば、Eric Evans の DDD 本（ エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) ）や、Vaughn Vernon の IDDD 本（ 実践ドメイン駆動設計 (Object Oriented SELECTION) ）が有名です。 DDDで登場する実装パターンや周辺知識をより実践的に解説しているのが、「．ＮＥＴのエンタープライズアプリケーションアーキテクチャ 第２版 (マイクロソフト公式解説書)」（naa4e）です。 .NET 以外にも大いに通じる内容 本書は、.NET の名を冠しており、サンプルコ…</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/4822298485/shinxblog-22&quot; class=&quot;asin&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/51LJmPXfDAL.jpg&quot; alt=&quot;．ＮＥＴのエンタープライズアプリケーションアーキテクチャ　第２版 (マイクロソフト公式解説書)&quot; title=&quot;．ＮＥＴのエンタープライズアプリケーションアーキテクチャ　第２版 (マイクロソフト公式解説書)&quot; class=&quot;asin&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DDD 関連の書籍といえば、Eric Evans の DDD 本（ &lt;a href=&quot;http://d.hatena.ne.jp/asin/4798121967/shinxblog-22&quot;&gt;エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)&lt;/a&gt; ）や、Vaughn Vernon の IDDD 本（ &lt;a href=&quot;http://d.hatena.ne.jp/asin/479813161X/shinxblog-22&quot;&gt;実践ドメイン駆動設計 (Object Oriented SELECTION)&lt;/a&gt; ）が有名です。&lt;/p&gt;

&lt;p&gt;DDDで登場する実装パターンや周辺知識をより実践的に解説しているのが、「&lt;a href=&quot;http://d.hatena.ne.jp/asin/4822298485/shinxblog-22&quot;&gt;．ＮＥＴのエンタープライズアプリケーションアーキテクチャ　第２版 (マイクロソフト公式解説書)&lt;/a&gt;」（naa4e）です。&lt;/p&gt;

&lt;h2&gt;.NET 以外にも大いに通じる内容&lt;/h2&gt;

&lt;p&gt;本書は、.NET の名を冠しており、サンプルコードは、C# で記述されています。また、利用するライブラリも .NET 周辺のものであったり、解説で登場する RDBMS も、Web 系でよく登場する MySQL や PostgreSQL よりも、SQL Server や Oracle がメインとなっています。&lt;/p&gt;

&lt;p&gt;しかし、.NET 固有の話はそれだけです。.NET 固有の内容は、枝葉の部分であり、メインの内容は、プラットフォームやプログラミング言語に関わらずとても参考になるものでした。&lt;/p&gt;

&lt;p&gt;日頃、.NET 界隈にいない人間としては、正直、マイクロソフト公式解説書としての体裁になっているがゆえに避けてしまう人がいるのが、勿体無いとさえ感じる本でした。&lt;/p&gt;

&lt;h2&gt;DDDをガラスケースから日々実践するプラクティスへ&lt;/h2&gt;

&lt;p&gt;DDD 本、IDDD 本と本書を比べると、より実践に寄せた内容となっており、DDD をより身近な普段の開発に適用するために解説されています。&lt;/p&gt;

&lt;p&gt;実際の開発では、上手くパターンにあてはめられなかったり、妥協してしまう場面もあるのですが、そのあたりも考慮に入れられています（トランザクションスクリプトも場面によっては有用なパターンである、など）。本書の表現を借りると、ヒーローがいなくても、一般の開発者が DDD 取り組む方法を示した本とも言えます。&lt;/p&gt;

&lt;p&gt;それぞれ執筆された時期が異なる（DDD 本（2003年）、IDDD 本（2013年）、本書（2014年））ので、理論から実践、さらに実践した上で現実的な落し込みといった具合に、より現実的な内容に推移しているのが興味深いです。&lt;/p&gt;

&lt;p&gt;私は、DDD 本、IDDD 本、本書の順序で読んだのですが、本書が一番理解しやすいと感じました。&lt;a href=&quot;#f-ce84bada&quot; name=&quot;fn-ce84bada&quot; title=&quot;もちろん、それは前 2 冊を読んで、多少なりとも実践したからこそというのはあります。&quot;&gt;*1&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;本書の内容&lt;/h3&gt;

&lt;p&gt;本書の目次は下記です。&lt;/p&gt;

&lt;p&gt;現代の設計手法や開発手法、OOPの基礎（SOLID原則など）やテスト手法からはじまり、DDDで利用されるパターンの解説に入っていきます。&lt;/p&gt;

&lt;p&gt;本書で興味深いのは、第6章のプレゼンテーション層がドメインアーキテクチャのすぐ後に来ている点と、CQRS と イベントソーシング（ES）に多くのページが割かれている点です。&lt;/p&gt;

&lt;pre class=&quot;code&quot; data-lang=&quot;&quot; data-unlink&gt;■第1部 基礎
第1章 現代のアーキテクトとアーキテクチャ
第2章 成功のための設計
第3章 ソフトウェアの設計原則
第4章 高品質なソフトウェアの作成

■第2部 アーキテクチャの考案
第5章 ドメインアーキテクチャの発見
第6章 プレゼンテーション層
第7章 伝説のビジネス層

■第3部 サポートアーキテクチャ
第8章 ドメインモデルの紹介
第9章 ドメインモデルの実装
第10章 CQRS の紹介
第11章 CQRS の実装
第12章 イベントソーシングの紹介
第13章 イベントソーシングの実装

■第4部 インフラストラクチャ
第14章 永続化レイヤー&lt;/pre&gt;


&lt;h3&gt;ユーザエクスペリエンス(UX)ファースト&lt;/h3&gt;

&lt;blockquote&gt;&lt;p&gt;ソフトウェアの世界では、バックエンドから取り組むのが慣例となっていました。私たちの多くは、プレゼンテーションを
システムのそれほど高尚な部分だと見ておらず、ビジネス層をデータアクセス層が完成してから取り組めばよい、という程度に考えていました。しかし、システムの複雑さを問わず、プレゼンテーションとバックエンドが等しく必要であることに疑問の余地はありません。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Webアプリケーションでもサーバサイドをメインにやっていると、プレゼンテーションの重要性は認識はしていても、重点をサーバ側に置いてしまいがちでした。&lt;/p&gt;

&lt;p&gt;しかし、プレゼンテーションはユーザが直接システムに触れるインタフェースであり、ここからシステムのユースケースやタスクを見ていくというのは当然のことです。&lt;/p&gt;

&lt;p&gt;自分が設計する際は、プレゼンテーションももちろん考えますが、どちらかと言えば、サーバサイドの設計を主に考えていた節があるので、ハッとさせられました。&lt;/p&gt;

&lt;h3&gt;CQRS を身近なパターンに&lt;/h3&gt;

&lt;blockquote&gt;&lt;p&gt;CQRSは、ソフトウェアアーキテクチャにおけるコロンブスの卵です ── すなわち、自明ではない問題に対する驚くほど明確でそつの ないソリューションです。開発者とアーキテクトは、DDDとDomain Modelの階層化アーキテクチャを理解し、正当化し、機械化すること に何年も費やしてきました。特に複雑なビジネスドメインでは、クエリスタックとコマンドスタックの両方をドメインモデルで実現する ことがきわめて難しいという問題と格闘してきました。
CQRSにより、すべてが一夜にして変わってしまい、複雑なビジネスドメインがはるかに管理しやすくなりました。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;IDDD 本では、やや特殊なパターンとして解説されていた CQRS が、多くの場合に適用できる身近なパターンとして解説されています。&lt;/p&gt;

&lt;p&gt;このあたりは、私もドメインモデルからクエリを分離して、クエリについては、DTOを返すリポジトリを作って実装した経験から、わりと使い勝手が良いパターンではないかという印象を持っていました。&lt;/p&gt;

&lt;p&gt;本文では、単なる CRUD パターンでも、CQRS にしてコマンドとクエリの関心事を分けるのは意味があると書かれており、CQRS に興味がある人には参考になる内容でしょう。&lt;/p&gt;

&lt;h3&gt;イベントソーシング（ES）と似たものは、30年以上前からある&lt;/h3&gt;

&lt;blockquote&gt;&lt;p&gt;先に挙げたアプリケーションはどれも数十年前から存在しているもので、COBOLやVisual Basic 6でかかれたものさえあります。つまり、イベントソーシングなんて仰々しい名前が付いていても、決してソフトウェアの新しい概念の到来を告げるものではないのです。&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;新たな名前が付いたものが出てくると、身構えてしまいますが、実は従来からあるものに名前が付いてパターン化したものというのも良くあります。&lt;/p&gt;

&lt;p&gt;その最たるものが、GoF でお馴染みのデザインパターンでしょう。イベントソーシングもこうした以前からあるものをパターン化したものです。&lt;/p&gt;

&lt;p&gt;現実世界での出来事では、発生した事象が消えてなくなることはなく&lt;a href=&quot;#f-708adead&quot; name=&quot;fn-708adead&quot; title=&quot;購入商品をキャンセルしても、購入した事実は消えない&quot;&gt;*2&lt;/a&gt;、事象をイベントとして残していくというのは特にユーザからすると理解しやすいものです。&lt;/p&gt;

&lt;p&gt;例えば、商品購入を記録する場合、購入情報のドメインモデルを保存し、ステータスが変わる度にそれを書き換えます。そして、イベントを補足情報としてログに記録するというのは良くあるパターンです。&lt;/p&gt;

&lt;p&gt;イベントソーシングでは、これを逆転し、イベントを記録する方を主とします。そして、保存したドメインモデルは、あくまでもスナップショットとして扱います。イベントには、必要な情報が全て記録されているので、原理的にはそれらを順に適用していけば、どの時点のドメインモデルでも再現できるからです。&lt;/p&gt;

&lt;p&gt;実際にイベントソーシングを実装するとなると、パフォーマンスの問題は無視できないのですが、ここについても実装のアイデアが解説されています。&lt;/p&gt;

&lt;h2&gt;さいごに&lt;/h2&gt;

&lt;p&gt;DDD 本を読んで、自分たちが取り組んでいるプロジェクトに DDD を実践してみたい人や、実際に取り組んでみたもののまだしっくり来ない人に手にとって見てもらいたい本です。&lt;/p&gt;

&lt;p&gt;ユーモアを交えて、平易な表現で書かれており、理解しやすいので、これから DDD を学ぶ人や OOP の基礎を見直す人にも良さそうです。&lt;/p&gt;

&lt;p&gt;実装で具体的なイメージを持った状態で、理論に立ち返るとより理解が深まるので、この本から DDD 本や IDDD 本に戻るのも良いですね。&lt;/p&gt;

&lt;p&gt;なお、この本を読んだ後に、Amazon でリコメンドされた「&lt;a href=&quot;http://d.hatena.ne.jp/asin/4822298477/shinxblog-22&quot;&gt;C#実践開発手法 (マイクロソフト公式解説書)&lt;/a&gt;」も気になる内容だったので、こちらも読んでみたいと思います。&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/B00ZQZ8JNE/shinxblog-22/&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/51c52CsHWTL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;.NETのエンタープライズアプリケーションアーキテクチャ第2版　.NETを例にしたアプリケーション設計原則&quot; title=&quot;.NETのエンタープライズアプリケーションアーキテクチャ第2版　.NETを例にしたアプリケーション設計原則&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/B00ZQZ8JNE/shinxblog-22/&quot;&gt;.NETのエンタープライズアプリケーションアーキテクチャ第2版　.NETを例にしたアプリケーション設計原則&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; ディノエスポシト,アンドレアサルタレロ&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 日経BP社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2015/06/18&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; Kindle版&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/B00ZQZ8JNE/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログを見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/B00GRKD6XU/shinxblog-22/&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/6181Uutb1tL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;エリック・エヴァンスのドメイン駆動設計&quot; title=&quot;エリック・エヴァンスのドメイン駆動設計&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/B00GRKD6XU/shinxblog-22/&quot;&gt;エリック・エヴァンスのドメイン駆動設計&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; Eric Evans&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 翔泳社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2013/11/20&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; Kindle版&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/B00GRKD6XU/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (4件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&quot;hatena-asin-detail&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/479813161X/shinxblog-22/&quot;&gt;&lt;img src=&quot;http://ecx.images-amazon.com/images/I/61Y5Po5i8lL._SL160_.jpg&quot; class=&quot;hatena-asin-detail-image&quot; alt=&quot;実践ドメイン駆動設計 (Object Oriented SELECTION)&quot; title=&quot;実践ドメイン駆動設計 (Object Oriented SELECTION)&quot;&gt;&lt;/a&gt;&lt;div class=&quot;hatena-asin-detail-info&quot;&gt;&lt;p class=&quot;hatena-asin-detail-title&quot;&gt;&lt;a href=&quot;http://www.amazon.co.jp/exec/obidos/ASIN/479813161X/shinxblog-22/&quot;&gt;実践ドメイン駆動設計 (Object Oriented SELECTION)&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;作者:&lt;/span&gt; ヴァーン・ヴァーノン,高木正弘&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;出版社/メーカー:&lt;/span&gt; 翔泳社&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;発売日:&lt;/span&gt; 2015/03/17&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;hatena-asin-detail-label&quot;&gt;メディア:&lt;/span&gt; 大型本&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://d.hatena.ne.jp/asin/479813161X/shinxblog-22&quot; target=&quot;_blank&quot;&gt;この商品を含むブログ (4件) を見る&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class=&quot;hatena-asin-detail-foot&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-ce84bada&quot; name=&quot;f-ce84bada&quot; class=&quot;footnote-number&quot;&gt;*1&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;もちろん、それは前 2 冊を読んで、多少なりとも実践したからこそというのはあります。&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn-708adead&quot; name=&quot;f-708adead&quot; class=&quot;footnote-number&quot;&gt;*2&lt;/a&gt;&lt;span class=&quot;footnote-delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;footnote-text&quot;&gt;購入商品をキャンセルしても、購入した事実は消えない&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;</content>        
        <link rel="enclosure" href="http://ecx.images-amazon.com/images/I/51LJmPXfDAL.jpg" type="image/jpeg" length="0" />

        <author>
            <name>shin1x1</name>
        </author>
    </entry>
    
  
</feed>
