随時開催 オンサイトトレーニング

PHPメンターズとは?

PHPプログラマー向けのサービスです

PHPメンターズは株式会社アイテマンとインクス株式会社が運営するPHPプログラマー向けのサービスです。PHPプログラマーやチームを対象にトレーニング、技術サポート、メンタリング等を提供しています。本サービスを通じてPHPプログラミングの枠を超えたより普遍的な技術や知識を多くの方と共有することが私たちの大きな目標です。

左から、インクス株式会社 後藤秀宣、株式会社アイテマン 久保敦啓

ブログ

「ドメイン駆動設計読書会@大阪」第8回復習メモ

プログラミング道場生 kumamidori です。

「ドメイン駆動設計」読書会の第8回に参加しました。内容は、こちらのWikiにまとめられています。

この記事の前半では、復習として、関連、エンティティ、値オブジェクトについて、自問自答のQ&A形式でメモします。この道の先人から教えて頂いた内容が多いです。教えて下さった方、ありがとうとざいました。後半で、勉強会後に見つけた値オブジェクトのPHPサンプルを引用して紹介します。


Q&A


関連

Q. 情報と情報の関連は、(双方向ではなく)できるだけ一方向にすること、関連を限定することにより、 管理しやすくなる、という話があった。適切に関連を見出せるかどうかは、業務分析にかかっている。間違いなくそれを行うことはかなり難しいのでは?

A. 一発ではできない。設計、実装しながらモデルを直していく(蒸留)。 関連は、業務のユースケースにより決まってくる。ユースケースのイベントにおいて関連は発生する。実装としては、ORMレベルでは双方向で定義しておき、それを利用するときに、関連の方向、I/Fを検討すれば良いかもしれない。


エンティティ

Q. データモデリング分野では、エンティティはどう定義されているか?

A. これについては、astah*チュートリアルに書いてあった内容を、下記に一部改変して要約します。
一部改変した要約:ここから

・・・・・・・・・・・・・・・・・・・・

エンティティとは、システムの管理対象となりうるもので、人、モノ、イベント、履歴などがある。 エンティティには、リソース系、イベント系、サマリー系などがあり、以下のように分類される。

  • リソース系: 会社、顧客、商品など。
  • イベント系: 会員登録・変更・削除、発注、出荷など。
  • サマリー系: 購入履歴、出荷履歴など。

「発注日」、「出荷日」のように、「〜日」を付けることができるのがイベント系エンティティと言える。

・・・・・・・・・・・・・・・・・・・・

一部改変した要約:ここまで

  • 例1:DoorKeeperのような勉強会告知サイトのシステムを運用するとして、ユーザが勉強会に参加申込をする、参加申込をキャンセルする、という機能があるとすると、これらの機能を実現するにあたって、「参加申込」情報はシステムの管理対象、エンティティとして扱います。 「参加申込エンティティ」は、動詞的な行為の記録であり、イベント系エンティティに見えます。
  • 例2:家計簿アプリを作るとして、月次予算・実績サマリー一覧画面があり、詳細画面へと遷移すると、該当月の予算を変更することができる、というような機能が必要だとします。ここでは、「月次収支サマリ」がエンティティとして扱われることになります。

Q. Web開発の現場において、エンティティは実装としてはどう扱われているか?

A. ORM的なライブラリを使っていることが多く、これがエンティティとして扱われている。 モデルを実装コードに反映するテクニックとして、たとえばユーザIDをそのままむき出しで表示側や業務ロジックで扱うのではなく、 いったんユーザエンティティに変換してから使うという工夫が採られる。DTO的な考え方。


値オブジェクト

Q. 「エンティティ」はみんな昔から使っているけれども、モデル駆動設計の構成要素として挙げられているパターンである「値オブジェクト」は、開発現場で見たことが無い。 値オブジェクトは、属性セットから成るが、DTOとはどう違うのか?

A. 値オブジェクトは、エンティティと同様に、モデルを表現し、ユビキタス言語とひも付く。情報を保持する役割を持つ。 エンティティと異なる点は、同一性、連続性を必要としないこと(識別が不要であること)。
いわゆるDTOとは文脈が違う。属性セットが主役になるという意味で、コードの見た感じがたまたまDTOと似た形になることもあるというだけ。


Q. 値オブジェクトの分かりやすい具体例としては、何が挙げられるか?

A. ユーザプロフィールの「住所」、「趣味」。eコマースの「Money」。レストランレビューサイトの「評価」。


Q. エンティティと値オブジェクトは関連するのか?

A. 値オブジェクトは、エンティティと直接関連する形になることが多いけれども、そうでないものもある。


値オブジェクトのPHPサンプル

PHPだと、初期化やバリデーションを考えると、気軽に値オブジェクトを定義する道具立ては無いそうです。現状のライブラリだと、自ドメインの値オブジェクトをエンティティにカスタム定義することは、できなくはないけれども、手間がかかるとのことです。(モデル側でのビューモデルや、ビュー側でのヘルパー機能を使って、ある程度はすでに工夫できているのが実情かもしれないと思いました)。調べたらサンプルが見つかったので、翻訳引用の形で紹介します。


PHPサンプル:Timeオブジェクト

Mathias Verraes さんがTimeオブジェクトのサンプルコードを紹介しています。元記事は名前付き引数についての考察なのですが、値オブジェクトと関係あるところだけを翻訳引用します。

翻訳引用ここから(原文記事リンク:Named Constructors in PHP

・・・・・・・・・・・・・・・・・・・・

(中略)

ユビキタス言語

コードは良い具合にきれいになってきました。時刻クラスは、大変使いやすい初期化の方法を持つようになりました。設計が良くなって、以前の設計上の欠陥はなくなり、分かりやすくなっています。時刻のインターフェイスを見てみて下さい。

<?php
$time = Time::fromValues($hours, $minutes);
$time = Time::fromString($time);
$time = Time::fromMinutesSinceMidnight($minutesSinceMidnight);

気づきましたか?3つの言葉を混同することがなくなりました。

  • fromString は、PHP実装の詳細です。
  • fromValues は、一般的なプログラミングタームでの並べ方です。
  • fromMinutesSinceMidnight は、ドメインの言語の一部です。

言語ギーク、DDDファンとしては、まだ納得がいきません。時刻はドメインの一部なので、ユビキタス言語によって取得を分けるスタイルの方が私は好きです。

  • fromString => fromTime
  • fromValues => fromHoursAndMinutes

文字数が長くなってしまうことを気にする人がいるかもしれないけれども、タイピングする必要は無いのです。コンテキストでコード補完のできるエディタを使いましょう。

・・・・・・・・・・・・・・・・・・・・

翻訳引用ここまで

※上記で翻訳紹介した「時刻」は、DDDの主戦場(?)であるアプリケーションのコアドメインではなくて、汎用的なライブラリ(コアドメインから利用される、外側のパッケージに属している)と思います。汎用ライブラリを自分がこれから作る、という際には、そこが自分にとってドメインとなるのかなと思っています。


参考記事



振り返り

読書会も8回を経て、さすがに基礎部分の理解はできてきました。(これはDDD本に直接書いてあることではないけれど、)読書会で聞いた「共通性」、「可変性」という言葉を知っているだけでも、実務でのクラス設計が良い方に変わったのを感じます。今回のような復習ブログを書くのは、今後はお休みにして、以降はもう少し、自分自身がコード等でアウトプットできるような形でやっていけたら良いなと思っています。(現状だと、ブログを書くと、誰かの引用だけで力尽きてしまう感じなので、そこからもう一歩進められたらと)。

「ドメイン駆動設計読書会@大阪」第8回感想

プログラミング道場生 kawakawaです。   

ドメイン駆動設計読書会の第8回に参加してまいりましたので、感想を報告させていただきます。   

    

読書会の内容は、レポート担当者さんにより、wikiにまとめて頂いております。   
第8回「ドメイン駆動設計読書会@大阪」公式レポートWiki  

 

第8回では、第5章の「エンティティ」「値オブジェクト」が範囲でした。 
(「サービス」は次回(第9回)予定) 

 

 

「エンティティ」と「値オブジェクト」

  

「エンティティ」「値オブジェクト」は、それぞれDDD本の用語解説から抜粋すると、  

エンティティ 

  • 本質的に、属性によってではなく、連続性と同一性の連なりによって定義されるオブジェクト。 

 

値オブジェクト 

  • ある特徴や属性を記述するが、同一性の概念を持たないオブジェクト 

以上のように記載されております。 

読書会でこの「連続性「「同一性ついて話題が上がりました。 

同一性とは、 

  • 概念的な同一と認識するもの 

  • 同一性は追跡できるようにしておくこと 

  • 同一性を間違えると、データ破損に繋がる 

DDD本には、同一性について以下の記載がされていました。 

   第5章 「エンティティをモデル化する」より抜粋 

  • 同一性の定義はモデルから現れる。 

  • 同一性を定義するには、ドメインを理解しなければならない 

 これは、同一性というのは絶対的なものでなく、ドメインによって変化するものという事を、暗示しているのだと思います。 

 

 また、連続性については 

  • ライフサイクル内において、同一性が続くこと 

  • 例えると、犬は年々成長し、体格は変わっていくが、その犬という概念は変化していない 

 つまり、一定期間(ライフサイクル)内で、同一性が保証される事が、連続性であると言えると思います。 

 

読書会で面白いと思った意見は、東京のミッキーマウスを叩いたら、アメリカのミッキーは痛がるのかどうかという話でした。 

単純に考えると、痛がれば「エンティティ」、痛がらないのなら「値オブジェクト」と言えそうですが、ミッキーは世界で一人(一匹?)という建前があるので、痛がらないといけないわけですね。 

 

エンティティと値オブジェクトの見分け方

  

エンティティと値オブジェクトの違いは、DDD本から抜粋すると、 

   第5 ソフトウェアで表現されたモデル」 

  • あるオブジェクトは、状態が異なったり、さらに別々の実装をまたいだりしたとしても追跡されるような、連続性と一意性を持ったものを表現しているのか? それとも、他の何かの状態を記述する属性なのか? これがエンティティと値オブジェクトとの基本的な区別である。 

とあります。  

そのまま抜き出すと、 

 エンティティ・・・実装をまたいでも、それと判る連続性と一意性を備えているもの 

 値オブジェクト・・・何等かの状態を記述する属性 

このように見る事ができます。 

 

また、第5「関連」では下記のように記載されています  

  • 多対多の関連を辿る方向を制限することで、実装は一対多へと効果的に減らされる。これははるかに容易な設計である。 

  • ドメインにおける重要度を反映させるように、関連を一貫して限定することによって、その限定された関連は、伝達力がより豊かになり、実装もシンプルになる。 

  • ある区別をすることによって、モデルが明確になり、より現実に即して実装できるようになる 

 

オブジェクト同士の関係を単純化していくことで、モデルが明確になっていき、 エンティティや値オブジェクトを見つけやすくなりそうです。 

しかし、モデルによっては同じオブジェクトでも、エンティティ・値オブジェクトが変わる可能性がある事を、意識しておく必要がありそうです。

何故「エンティティ」と「値オブジェクト」を分ける必要があるのか?

 

オブジェクトをすべて、エンティティとみなす事への注意点として、下記のように記載されております。 

第5 「値オブジェクト(VALUE OBJECTS)」より抜粋 

  • エンティティの同一性を追跡するのは本質的なことだが、それ以外のオブジェクトに同一性を与えてしまうと、システムの性能を損なうことになり、分析作業が増え、さらに、すべてのオブジェクトの見た目が同じになってしまうことでモデルが台無しになりかねない 

 

ドメインにとって、必要以上にエンティティを設けると、間違いを犯したり、分析に時間がかかってしまうなど、モデルが台無しになってしまうようです。 

無駄なオブジェクトの省き、モデルを単純化するという作業の中には、このようにエンティティを見極めるという作業も含まれている事を知ることが出来ました。

 

 

エンティティとデータベースの関係性

 

 エンティティのDB登録で、Ruby on RailsのようなActiveRecordパターンを使った実装話で盛り上がりました。 

 話の趣旨としては、 

  • SQLを書くと、間違いやミスが発生しやすい 

   (IDEによる補助を受けらない場合が多いから 

  • SQLでは、DB実装を意識させられてしまう 

  • DBの構造と、エンティティや値オブジェクトの構造に差異がある場合が多い 

 などの理由により、如何にSQLを書かずに、実装できるかという内容でした。 

個人的には、モデルの変更スピード、コードの変更スピード、DBの変更スピードそれぞれの差異による、モデルとDB設計剥離が、目下の課題と考えておりましたので、大変勉強になりました。 

コード・ファーストがその解に近いと思っておりますが、まだ考えがまとまっていないので、この点は改めて考えてみたいと思います。 

 

BEAR.Sundayチュートリアル・スクリーンキャスト

プログラミング道場生 kumamidori です。

5月31日に、Nagoya.php vol.5 に参加しました。

vol.5 のテーマは、「PHPフレームワークの1つであるBEAR.Sundayを触ってみよう」でした。 参加者全員で、BEAR.SundayワークショップWiki(チュートリアル)の内容に取り組みました。 私が見つけられた範囲だと、@kenji_sさん、@qckanemoto さんがブログにまとめられていますので、そちらをご覧頂ければと思います。記事の文末にそのリンクを貼ります。

BEAR.Sundayチュートリアル・スクリーンキャスト

この記事では、スクリーンキャストで、BEAR.Sundayチュートリアルのデモを、初心者なりの解説をまじえつつ紹介します。(見る人、誰もいないかもしれませんけれど・・・。)

※今回動画を掲載することについて、フレームワーク作者に事前に許可を頂いております。

PHPカンファレンス関西について

BEAR.Sundayの作者である郡山さんは、6月28日(土)に開催されるPHPカンファレンス関西2014の基調講演をされる予定です。関西方面でご興味のある方は、懇親会の方にも申し込みされると良いかと思います。@hidenorigotoさんも遠方から来訪、参加されるそうです。

参考リンク

BEAR.Sundayに関するリンク
スクリーンキャスト内で引用した書籍

PHPメンターズブログにおける関連記事
Nagoya.php vol.5 に関するリンク

※6月29日追記:

名古屋の方から、「写真、撮っていましたよね?」とリマインドを頂いたので、貼っておきます。

ドメイン駆動設計のリポジトリパターンをプロジェクトへ持ち込む時の話

プログラミング道場生Hatajoeです。 ドメイン駆動設計読書会@大阪には初回から参加させて頂いています。 今回は、読書会で得られた知見を業務に導入する際の気付きをお話したいと考えています。

なぜリポジトリなのか

話をする前に、なぜドメイン駆動設計な開発を導入しようと思ったのかの前提を説明させて下さい。

私は普段、チームでWebアプリケーションの開発を行っています。 チームには、自分を含めて3名のプログラマーが在籍しています。 言語はPHPで、CodeIgniterというWebアプリケーションフレームワークを使用しています。

現在、私達は以下の問題を抱えています。

  • ファットコントローラー
  • コピペコード
  • テスト無し

既存機能の改修難易度が高く、機能追加でレガシーなソースが量産されるという悪循環です。 私は、これに対処するために、複雑で重複したビジネスロジックを切り出す必要があると考えました。 しかし、切り出されたビジネスロジックを如何に扱うかという点に悩まされ、 余計に手に負えないソースが散らばるという悲惨な状態になったりもしました。

ドメイン駆動設計について学びを進めていくと、ビジネスロジックの扱いについて、1つの個人的な知見を得ることが出来ました。 それは、ビジネスロジックはユビキタス言語の実装であり、ユビキタス言語は「モデル」の振る舞い(API)である。 そして、「モデル」は抽象化されたデータと振る舞い(API)から成る、という知見です。

さて、上記で示した「モデル」ですが、実際に実装するにためにはまだ部品が足りていないことに気が付きます。 それは、アプリケーションコードの中で「モデル」をどのように扱うのか、という点です。 そこでリポジトリが登場します。

実際に、「モデル」を使用したアプリケーション実装を行ってみると、仕様から「モデル」を切り出す作業が最も難しいと感じます。 「モデル」を切り出す作業には答えがありません。 日々、改善を繰り返す部分だと思っています。(出来るか出来ないか、は置いておいて…)

一方で、リポジトリの実装はパターン化出来るのではないかと考えています。 例えば、チームメンバーに自分と同じ負担を強いるのは現実的ではありません。 チーム開発においては、各々、自身が得た知見をチームのコンテキストに沿って還元することが必要だと感じています。 そのため、「モデル」を切り出す時に必要な概念的複雑さはひとまず置いておいて、未熟な「モデル」で機能を実装出来る方法。 この時、リポジトリは非常に重要な存在になるように思えたわけです。

リポジトリの役割

エリック・エヴァンス氏は、リポジトリをモデルのライフサイクルを管理するオブジェクトだと定義しています。 ライフサイクルを管理する、ということが一体どういったことなのか。 自身の開発に落としこんでみると、ファットコントローラーからビジネスロジックとデータを切り出したそれらを扱うグルーのような存在であることに気が付きます。

ここで、説明を単純化するために、ビジネスロジックとデータを切り出して作り上げたオブジェクトを「ドメインクラス」と呼ぶことにします。 ドメインクラスについての詳しい説明はひとまず置いておいて、リポジトリの役割について注目します。

端的にリポジトリの役割を示すと以下のようになるかと思います。

  • リポジトリは、ストレージからデータを取得し、ドメインクラスにデシリアライズする。
  • リポジトリは、ドメインクラスをシリアライズし、ストレージに保存する。
  • リポジトリは、ビジネスロジックを持たない。

これまでは、コントローラーからデータを直接取得していたところ、リポジトリを使用して、ドメインクラスを取得するように設計します。 これは、コントローラーがモデルの振る舞い(API)を利用してデータ操作(仕様)を実現するという形です。 こうすることで、仕様変更(データの変更)はリポジトリ内に局所化されるようにし、また、ビジネスロジックはドメインクラスに局所化するように設計します。(これはこれで結構難しいですが。。)

ドメインクラスがモデルのインターフェースとして機能し、それはユビキタス言語を実現したモデルのAPIとして振る舞い、データを隠蔽する。 リポジトリは、これらの一端を担う重要なファクターになると思います。

リポジトリの実装

以下を実装することになります。

  • データストレージからデータを取得してドメインクラスを生成する
  • ドメインクラスからデータを受け取ってデータストレージへ保存する

CodeIgniterを使用する場合、データストレージへのアクセスはCodeIgniterのモデルクラスを使用します。 リポジトリは、CodeIgniterのモデルクラスを利用してデータストレージからデータを取得し、ドメインクラスを生成します。

<?php 

// とあるドメインクラス
// RPGのプレイヤークラスとする
class Player
{
    private $id;
    private $name;
    private $items;

    public function __constract($id, $name) {
        $this->id   = $id;
        $this->name = $name;
    }

    public function get_id() { return $this->id; }
    public function get_name() { return $this->name; }
    public function set_items($items) { $this->items = $items; }
    public function get_items() { return $this->items; }
}

// とあるドメインクラス
// RPGのアイテムクラスとする
class Item
{
    private $id;
    private $name;
    private $num;

    public function __construct($id, $name, $num) {
        $this->id   = $id;
        $this->name = $name;
        $this->num  = $num;
    }

    public function get_id() { return $this-id; }
    public function get_name() { return $this->name; }
    public function get_num() { return $this->num; }
    public function consume() { $this->num -= 1; }
}

// プレイヤーリポジトリ
// CodeIgniterのモデルクラスからデータを取得し、Playerを生成する
class PlayerRepository
{
    private $CI;

    public function __construct($CI) {
        $CI->load->model('Tbl_player');
        $this->CI = $CI;
    }

    // プレイヤーのみ取得
    public function resolve_by($player_id) {
        $p = $this->CI->Tbl_player->find_by_id($player_id);
        return new Player($p->id, $p->name);
    }

    // プレイヤーと所持アイテムを取得
    public function resolve_with_items_by($player_id, $item_repository) {
        $player = $this->resolve_by($player_id);
        $items = $item_repository->resolve_by($player_id);
        $player->set_items($items);
        return $player;
    }
}

// アイテムリポジトリ
// CodeIgniterのモデルクラスからデータを取得し、Itemを生成する
class ItemRepository
{
    private $CI;

    public function __construct($CI) {
        $CI->load->model('Tbl_item');
        $this->CI = $CI;
    }

    // プレイヤーの所持アイテムを取得
    public function resolve_by($player_id) {
        $items = [];
        foreach ($this->CI->Tbl_item->find_by_player_id($player_id) as $i) {
            $items[] = new Item($i->id, $i->name, $i->num);
        }
        return $items;
    }
}

ドメインクラスを保存する場合はリポジトリ経由でCodeIgniterのモデルクラスへデータを渡し保存します。

<?php 

class ItemRepository
{
    public function register($item) {
        $this->Tbl_item->update($item->get_id(), $item->get_num());
    }
}

まとめ

ドメイン駆動設計と言いながら、肝心なドメインモデルの設計・実装は必要最小限に止め、まずは導入してメリットを得るためにリポジトリパターンにフォーカスしました。必ずしも、ドメイン駆動設計という枠の中で開発を行わなければいけないということはないと思います。 重要なのは、今出来る改善をチームで実行出来る状態にすることだと考えています。 自身のコンテキストに沿ったドメイン駆動設計を考えてみると、案外手近なところに改善案が見つかるかもしれません。

参考

Practical DDD #4: ユビキタス言語とモデル

ドメイン駆動設計のユビキタス言語とモデルの関係性についての、個人的なメモです。


思考を図にする

書籍「エリック・エヴァンスのドメイン駆動設計」の第2章で、ユビキタス言語の語彙について説明した段落があります。

ユビキタス言語の語彙には、クラスや主要な操作の名前が含まれている。また、モデルの中で明示されたルールについて議論するための用語も含まれている。この言語は、モデルが従うべき高次の構成原理(コンテキストマップや大規模な構造など、第14章と第16章で説明するもの)に由来する用語によって補完される。そして最後は、ドメインモデルに対して一般に適用されるパターンの名前によって、この言語は強化される。

エリック・エヴァンスのドメイン駆動設計
第2章 コミュニケーションと言語の使い方 ユビキタス言語 (p. 25)

この文章が言わんとしていることを私の理解で図にしてみました。


ユビキタス言語とモデルの関係

この図は、上に挙げた段落の意味を、パターンランゲージのナビゲーションマップの図法(DDD本の見開きにあるもの)をイメージして描いたものです。あくまで上の段落からの意味だけなので、ドメイン駆動設計のある1つの側面を表しているにすぎません。ただ、こうやって図にしてみることで、私には、書き手の思考がより伝わってくるように感じました。


これは概念モデリング?

先ほど私がやったこと、それは、ドメイン駆動設計という本に書かれた内容を解釈し、それが意味していると考えられる内容を、言葉と言葉というよりもむしろ、概念と概念の関係性としてナビゲーションマップ形式の図に表しました。要するにこれは、概念モデリングそのものではないでしょうか。

(Googleで検索してみると、同じようなことをされている方はすでにいらっしゃいます ex 「読書ツールとしての概念モデリング - 反言子」)

哲学者フォーダーによれば、

文は思考を、語は概念を表わす。(Fooder 1998)

信原 幸弘、太田 紘史 編『シリーズ 新・心の哲学I 認知篇』勁草書房 2014年
第1章 概念の構造とカテゴリー化 5-1 情報原子論と文法的現象 p. 61

というように、文章で書かれたものと、その文章から概念モデリングした図とは、その根底にある思考は共通しています。

で。。。。?

ドメイン駆動設計を軸にしたソフトウェアの分析・設計では、このように見出した概念の意味にこだわります。エヴァンスの本を読んでいると、まるで、言語体系をつくり上げること自体が設計の中心であるかのように錯覚することが私にはあります(私はまだ、そういった境地ではありません)。

ただ、ソフトウェアを設計するのに、そのような意味論にこだわりすぎるのが良い結果を生むのかどうか。今の私は結論を持っていません。


ドメイン駆動設計を読んでいる皆さんが、今回挙げた私の図を見て感じた違和感でも何でも、コメントいただけると幸いです。


参考