Be Framework Blog

ずばり Be Framework とは何か

Objects don’t DO things — they BECOME things.

名もなきif文の正体

あなたのコードベースを開いてみてください。いくつのif文がありますか。

if ($user === null) { ... }
if (!$user->isActive()) { ... }
if ($order->getStatus() !== 'confirmed') { ... }

これらのif文には名前がありません。テストもない。誰が書いたかも覚えていない。しかし消すと壊れる。なぜこれほど多くのガード文が必要なのでしょうか。

答えは単純です——不正な状態が型として表現可能だから。

User型は、有効なユーザーも無効なユーザーも、削除済みのユーザーも、存在しないユーザーすらも表現できてしまいます。だから「この状態で呼ばれたら?」というif文を至る所に書くことになる。

Be Frameworkは問いを変えます。不正な状態を表す型が存在しなければ、ガード文は要らない。

OOPが失ったもの

OOPは本来、自律したオブジェクトがメッセージで協調する世界を目指しました。

しかし実際に起きたのは逆でした。サービス層が「保存しろ」「通知しろ」「検証しろ」と命令を出し、オブジェクトは従順なデータの入れ物になった。23個のメソッドが常に同時に利用可能な$userは、ユーザーを表してなどいなかった——Doingの束だった。

// サービス層が外から操作する
$triageService->assess($patient);
$patient->setStatus('emergency');
$erService->assign($patient);

誰でも、いつでも、どんな患者にもsetStatus('emergency')を呼べます。経過観察の患者に救急室を割り当てることもできる。型はそれを許してしまう。

HOWでもWHATでもなく、WHETHER

手続き型は HOW(どうやるか)を問いました。OOPは WHAT(それは何か)を問いました。

Be Frameworkが問うのは WHETHER——そもそも存在できるか。

// 従来:存在した後にif文で検査する
$patient = new Patient($temp, $hr);
if ($triageService->isEmergency($patient)) {
    $patient->setStatus('emergency');
}

// Be:存在そのものが答えになる
$patient = new PatientArrival($temp, $hr);
$result = $becoming($patient);
// $result は EmergencyCase か ObservationCase。それ以外は存在しない。

EmergencyCaseにはassignER()があります。ObservationCaseにはassignWaitingArea()があります。経過観察の患者に救急室を割り当てるメソッドは存在しない——存在できないものは存在しない。

時間の回復

OOPには時間がありません。$user->delete()の後に$user->getName()が呼べます。さなぎは卵に戻れないのに、コードの中では何でもあり得る。

Be Frameworkはオブジェクトに時間を取り戻します。

PatientArrival → TriageAssessment → EmergencyCase

各型はある瞬間の存在を表します。PatientArrivalは到着時の存在、TriageAssessmentは判定中の存在、EmergencyCaseは緊急と確定した存在。過去に戻ることはできない。

内在 + 超越 → 新しい内在

変容は一つの公式に従います。

final readonly class TriageAssessment
{
    public Emergency|Observation $being;

    public function __construct(
        #[Input] float $bodyTemperature,  // 内在:前の存在から受け継ぐもの
        #[Input] int $heartRate,          // 内在
        #[Inject] JTASProtocol $protocol  // 超越:外から与えられる力
    ) {
        $urgency = $protocol->assess($bodyTemperature, $heartRate);
        $this->being = ($urgency === 'emergency')
            ? new Emergency()
            : new Observation();
    }
}

内在(#[Input])が超越(#[Inject])と出会い、新しい内在が生まれます。小麦粉がイーストと熱に出会ってパンになるように。ぶどうが酵母と時間に出会ってワインになるように。

どれほど複雑なシステムでも、どこを切り取っても同じ構造です。誰もコントロールしていない。それぞれが自律し、系を形成する。

自らの運命を宣言する

#[Be([TriageAssessment::class])]
final readonly class PatientArrival { ... }

#[Be]は「私は何になるか」の宣言です。オブジェクト自身が運命を宣言する。

呼び出しは常にこの1行です:

$result = $becoming(new PatientArrival($temp, $hr));

複雑さは中にある。外から見ると、これだけ。

Be, Don’t Do

Tell, Don’t Ask がOOPの原則なら、Be, Don’t Do はその先にあります。

命じるのではなく、存在を定義する。何をするか(Doing)ではなく、何であるか(Being)。

$emailと書いた瞬間に、その変数は正しいメールアドレスしか受け入れません。たまたま正しいのではなく、必然的に正しい。正しくないものはそもそも存在できない。

これがBe Frameworkです。