ドメイン駆動設計
PHP7
型宣言

PHPカンファレンス2017@東京_メモ②型を意識したPHPアプリケーション開発

More than 1 year has passed since last update.

PHPの型システム

 変数自体には型は無い
 値の方に型を持つ 
  gettype()で型を調べられる
 動的型付け言語
 暗黙的型変換
  10 + “5” = 15
   stringをintに自動で変換されている
  10 + “5e” = 15
   数値じゃない値が出てきたところで切って自動的にintに変換
  10 + “5e2” = 510
   5e2:指数表記になっている
 型宣言
  function double(int $i) : int {}
  引数や戻り値に型を指定出来る
   関数に入った瞬間はint型を保証
   戻り値もint型を保証
   double(“5e”) // TypeError
   型宣言使うならstrictモードをオンにする
    declare(strict_types = 1)
    
 まとめ
  値が型を持つ
  暗黙的型変換で柔軟に開発
  型宣言で厳密に開発
   ユーザーが選択(使い分け)出来る

型宣言を利用したコード実装

 Demo
  型宣言なし
  スカラー型で型宣言
  ユーザー定義型で型宣言

ユーザー定義型の例

<?php
//strictモードオン
declare(strict_types=1);

//税込価格計算クラス
final class priceWithTaxRateCalculate{
    public static function calculate(Price $price, TaxRate $taxRate): PriceWithTax //戻り値は税込価格型にする
    {
        return new PriceWithTax(intval($price->asInt() * (1 + $taxRate->asFloat())));
    }
}

//プライス型
final class price
{

    private $value;

    public function __construct(int $value)
    {
        $this->value = $value;
    }

    public function asInt():int
    {
        return $this->value;
    }
}

//税率型
final class taxRate
{

    private $value;

    public function __construct(float $value)
    {
        //消費税が1以下で無い場合は例外
        if(!($value < 1)){
            throw new InvariantException('Invalid taxRate is given');
        }
        $this->value = $value;
    }

    public function asFloat():float
    {
        return $this->value;
    }
}

//税込価格型
final class priceWithTaxRate
{

    private $value;

    public function __construct(int $value)
    {
        $this->value = $value;
    }

    public function asInt():int
    {
        return $this->value;
    }
}


?>

ドメイン特化型の実装Tips

 アプリケーションドメインを表現
 ドメインルールを検証
 型宣言による表現と強制
  インスタンスの生成 = ルールを満たしていることを保証
 PHPdockは実行時は役に立たない
 型宣言で表現できる範囲は限定される
  両方を組み合わせて使う

安易に継承を使わない

 継承元クラス型への適合を防ぐ
  継承元のクラスをそのまま受け継いでしまう
  PriceWithTaxがPriceを継承していた場合
   税込価格を商品価格として渡してしまう場合チェックが効かない
 継承元メソッドが含まれてしまう
  必要の無いメソッドが大量に含まれるクラスが出来てしまう
   出来るだけドメインに特化したメソッドのみにする
 実装を共有したい場合
  委譲やトレイトで共有

イミュータブルにすると安全

 完全コンストラクタ
 不変条件の検証が一度で済む
  コンストラクタで値をセットしても、setValue()みたいなメソッドがあると値がいつでも変えられてしまう
 ミュータブルな場合は都度検証

ファクトリメソッド

 newの乱立を防ぐ
 コンストラクタをprivateに
 

最後に

 型宣言で型を強制
 ドメインを型で表現
 型で気持ちいい開発を!

質疑(一部)

 Q;ドメイン特化型を導入した場合どのくらいパフォーマンスに影響があるか
 A:ケースバイケース、確かにスカラー型の方がメモリは食わないが優先度による
 Q:型を使った実装を少しずつ導入する場合どこから始めればいいか、単体テストもやって無い状況
 A:数値で仕様が決まっている部分、日付など。小さなクラスを作ってそれの単体テストを書くところから
 Q:PHP7.1でnullableが入ったが、エラーの場合例外で返すか、nullで返すか
 A:基本的にはnullで返すのはあまりよろしく無い。例外で返した方がわかりやすいのではないか。nullが返ってきた場合、使う側も扱いづらい。nullオブジェクトを定義する等。
 Q:ドメイン型からデータベースの型への変更をどうするか
 A:Repository層で処理。DBから取得した値を特化型に変換して返す等。
 Q:ソースの別プロジェクトへの流用がやりにくいのでは無いか
 A:流用すると、使わないメソッド等が入ったクラスが出てきてしまうため、ドメイン特化型の方がいい。ただ全てを特化型にすると時間がかかるので、ケースバイケースで対応。