【実務・中級編】Shape型とEnumの組み合わせによるドメインモデリングの最適解 – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

Hackの真髄:ShapeとEnumで構築する「型安全なドメイン」の極意

Hackという言語の真価は、PHPの柔軟な動的性を「コンパイル時に完全に封じ込める」厳格な型システムにある。多くのエンジニアがHackを使いながらも、連想配列(array)の海で溺れ、実行時エラーに怯えている。

今日ここで話すのは、HHVMの型チェッカーを最大限に活用し、「実行時に一度も型チェックを書かずに済む」ための、ShapeとEnumを組み合わせたドメインモデリングの最適解だ。

—

なぜ「配列」をそのまま扱うのが罪なのか

実務において、APIレスポンスやデータベースからの取得結果を `dict` で持ち回る設計は、技術的負債の最たるものだ。`mixed` は型安全性を放棄する宣言に等しい。

HHVMの型チェッカーは、`shape` を使うことで、その連想配列が「どのキーを持ち、どの型の値を持つか」を完全にトレースする。ここに `Enum` を加えることで、値の取りうる範囲を列挙型によって決定的に制約できる。

これが、我々が目指すべき「コンパイル時にバグが消滅する」世界だ。

—

実践:EnumとShapeによるドメインモデル設計

例えば、決済ステータスを持つ複雑な注文データを扱うシーンを想定しよう。文字列や整数で状態を管理するのではなく、明確な境界線を引く。

namespace App\Domain;

// 1. Enumで状態の取りうる値を完全に規定する
enum PaymentStatus: string {
PENDING = ‘pending’;
PAID = ‘paid’;
FAILED = ‘failed’;
}

// 2. Shapeでデータ構造を厳格に定義する
// readonly属性を付与することで、不変性を担保し最適化を促進する
type OrderData = shape(
‘id’ => int,
‘status’ => PaymentStatus,
‘amount’ => float,
‘currency’ => string,
);

final class PaymentProcessor {
/

  • 型チェッカーがこのメソッドの入力を完全に検証する。
  • 呼び出し元でこの構造を満たさない値があれば、ビルド時にエラーとなる。

/
public function process(OrderData $order): void {
// switch式でEnumを網羅的にハンドリング
// もしPaymentStatusに新しい値が増えた場合、型チェッカーがここでの修正漏れを警告する
$message = match($order[‘status’]) {
PaymentStatus::PENDING => ‘処理待ちです’,
PaymentStatus::PAID => ‘決済完了’,
PaymentStatus::FAILED => ‘決済失敗’,
};

echo “Order {$order[‘id’]} is {$message}\n”;
}
}

—

この設計がもたらす3つの「圧倒的メリット」

1. 「網羅性」の強制

`match` 式と `Enum` の組み合わせは最強だ。もし将来的に `REFUNDED`(返金済み)という状態を追加したとき、`match` 文でハンドリングを忘れていれば、HHVMの型チェッカーは容赦なくビルドを止める。これにより、実行時の「予期せぬ状態」を論理的に排除できる。

2. メモリ効率とパフォーマンス

HHVMは、型が確定している `shape` に対して、内部的にクラスに近い構造最適化を行う。`dict` として扱う場合に発生する動的なハッシュテーブルルックアップと比較して、アクセス時のオーバーヘッドは極限まで抑えられる。

3. リファクタリングの耐性

フィールド名や型を変更したい場合、IDEの補完と型チェッカーに従うだけで良い。grepで全ファイルを探すような原始的な作業は、もう過去のものだ。

—

現場のテクニカルリードからの助言

最後に、実務でこのパターンを導入する際の注意点を授ける。

  • API境界での変換: 外部からのJSON入力をそのまま `shape` にキャストするのは危険だ。必ず `Shapes::idx` やバリデーションライブラリを用いて、境界線で「未知のデータ」を「型安全なドメインモデル」へと昇華させよ。
  • 不必要な巨大化を避ける: `shape` があまりに巨大になる場合は、それは「オブジェクト(class)」に切り出すべきサインだ。あくまで `shape` は「データの受け渡し(DTO)」のための軽量な箱であると割り切ること。
  • Optionalなキーの扱い: `shape(?’key’ => int)` のように、存在が不確定なフィールドを定義できるが、安易に使うな。存在するかどうかを毎回チェックするコードを書くくらいなら、別の `shape` に分けるか、`Maybe` パターンを検討すべきだ。

結論

Hackの型システムは、あなたの「書くべきコード」を減らし、「守るべき約束」を強固にするために存在する。`dict` や `array` という魔法の箱から脱却し、`shape` と `Enum` でドメインをモデル化せよ。

型チェッカーが何も言わなくなったとき、あなたのアプリケーションは、実行時に一切の論理矛盾を起こさない「美しい時計の歯車」のように動き始めるはずだ。

さあ、コードを書き換えろ。コンパイルエラーこそが、あなたの最も信頼できる同僚なのだから。

タイトルとURLをコピーしました