【実務・中級編】Hackの`newtype`を用いた型エイリアスによるドメインプリミティブの作成 – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

Hackの `newtype` で実現する「型によるドメイン駆動設計」:プリミティブ執着からの脱却

コードレビューをしていると、未だに多くのプロジェクトで `string` や `int` の海に溺れているコードを見かける。「IDは単なる文字列だから `string` でいい」という慢心は、システムの規模が大きくなるにつれ、必ず致命的なバグとして帰ってくる。

Hackの `newtype` を使いこなせば、コンパイル時に型チェッカーが「意味的な誤用」を弾き飛ばしてくれる。今回は、実務レベルで即座に採用すべき、型安全を極めるためのドメインプリミティブ構築術を伝授する。

—

1. なぜ `type` ではなく `newtype` なのか?

Hackには `type`(型エイリアス)と `newtype`(不透明型)が存在する。多くのエンジニアが混同しているが、両者の境界は「抽象化の強制力」にある。

  • `type`: 単なる別名。`type UserId = string` と定義しても、`UserId` は `string` と同義であり、`string` を要求する関数に `UserId` を渡しても何ら文句は言われない。
  • `newtype`: 不透明型。定義したモジュール内でのみ内部構造が見える。外部からは「何らかの型」としてしか扱えず、明示的な変換関数を通さない限り、元の `string` や `int` と混ぜることは不可能だ。

これこそが、ドメインロジックにおける「最強の防壁」である。

—

2. 実践:強固なドメインプリミティブの構築

ユーザーIDと注文IDが共に `string` である場合、引数の順序を間違えても型チェッカーは黙認する。これを `newtype` で隔離しよう。

namespace Domain;

// 外部からは内部の string 構造は見えない
newtype UserId = string;
newtype OrderId = string;

final class IdFactory {
// コンストラクタを隠蔽し、ファクトリ経由で生成させるのが鉄則
public static function fromString(string $id): UserId {
// ここでバリデーションを挟むのが肝
if ($id === ”) throw new \InvalidArgumentException(‘ID cannot be empty’);
return $id;
}
}

function processOrder(UserId $user, OrderId $order): void {
// 誤って OrderId を UserId の位置に渡すと、HHVMが即座にエラーを吐く
// 開発者のミスをコンパイル時に潰せる、これぞ型安全の極み
}

—

3. パフォーマンスとランタイムへの影響

「型を複雑にするとHHVMの実行速度が落ちるのではないか」という懸念を持つ者がいるが、答えはNOだ。

HHVMのJITコンパイラは、`newtype` を型消去(Type Erasure)する。実行時には単なる `string` や `int` としてメモリに展開されるため、実行時のオーバーヘッドはゼロに近い。

ただし、注意点が一つ。「境界での検証」を怠るな。
外部APIからの入力やDBからの読み出し時に、必ず `from…` 系の静的メソッドを通し、型を昇格(Lift)させよ。そこさえ通過すれば、アプリケーションの深部では「型によって保証された正しい値」だけが流通する。

—

4. 実務で光る「型変換」の設計パターン

`newtype` を扱う際、最も面倒なのが「型をアンラップして元の値に戻す」処理だ。これを各所に散らばらせるとコードが汚染される。以下のように `asString()` メソッド等のインターフェースを統一せよ。

newtype EmailAddress = string;

final class Email {
public static function create(string $raw): EmailAddress {
if (!filter_var($raw, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException(‘Invalid email format’);
}
return $raw;
}

// 外部出力用(シリアライズやDB保存)
public static function toString(EmailAddress $email): string {
return $email;
}
}

この設計により、ビジネスロジックは「`EmailAddress` という概念」を操作することに集中でき、データ構造の詳細は境界線上に押し込めることができる。

—

結論:型はドキュメントを超えた「契約」である

コメントで「この変数は `UserId` です」と書くのは時間の無駄だ。そんなものは型チェッカーが理解できない。`newtype` を使えば、それはコンパイラレベルで強制される「契約」へと昇華される。

君たちがコードレビューで見るべきは、複雑なロジックそのものではない。「プリミティブな型が、いかにドメインの言葉へと変換されているか」だ。

型を惜しむな。`newtype` で境界を定義せよ。そうすれば、深夜3時のデプロイで、君が頭を抱えることはなくなるはずだ。

—
Hackを掌握せよ。型は、君を自由にするための最も強力な武器なのだから。

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