境界は痛みの先にある

正直に告白する。

私は、疎結合なコードが書けなかった。もっと正確に言えば、書けないことにすら気づいていなかった。

「書けなかった」と過去形で書いた。では今は書けるのかと聞かれたら、答えに詰まる。あの頃よりはマシになった。少しだけ。ただし、それは痛みの分だけだ。

きっかけは、趣味で書いていた小さな会計ソフトだった。副業の帳簿をつけるだけのツール。

動いていた。テストも通っていた。帳簿もちゃんとつけられていた。何も困っていなかった。

なのに——深夜にコードを読み返して、息が詰まった。

ひとつの関数を追いかけると、別のモジュールに飛ぶ。仕訳を作成する処理のはずが、いつの間にか請求書の生成ロジックに手を突っ込んでいる。勘定科目のマスタ取得と入金ステータスの判定が、なぜか同じクラスに住んでいる。

何かが違う。しかし、何が違うのかを言葉にできなかった。

違和感はある。でも言語化できない。これが、いちばん苦しい。

この記事は、あの夜の私と同じ場所にいる人に向けて書いている。「何かが違うのは分かってる。でも、どう直せばいいのか分からない」——密結合なコードの前で途方に暮れている人へ。

「疎結合にしろ」は呪文ではない

疎結合にしなければならない。それは分かっている。モジュール間の依存を減らし、変更の影響範囲を閉じ込める。教科書的な正解は知っている。

だが、何をもって「モジュール」とするのか。その境界線を、私は引けなかった。

コードの匂い(smell)を嗅ぎ取れるようになるには経験がいる、とよく言われる。メソッドが長い、クラスが大きい、依存関係が不自然——表面的な指標は知っていた。しかし、私が感じていたのは、もう少し根の深いものだった。そもそも、このコードが「何を」「どの単位で」切り分けているのかが、間違っている。仕訳と請求と入金確認が一つのトランザクションの中で絡み合っている。解きほぐそうとしても、どこから手をつけていいか分からない。

Evans本を読んだはずだった

答えが出ないまま、昔読んだEric Evansの『ドメイン駆動設計』を思い出した。

分厚い本だった。境界づけられたコンテキスト。ユビキタス言語。集約。エンティティとバリューオブジェクト。読んだときには概念を理解できた気がした。少なくとも、本を読んでいるあいだは。

特に印象に残っていたのは、「暗黙的な概念を明示的にする」という考え方だ。ドメインの中には、関係者が当たり前のように使っているが、誰も明確に定義していない概念がある。その暗黙の概念をコードの中に明示的に表現すること——Evansはそれが設計の核心だと書いていた。

読んだときは何度も頷いた。まさにそれだ、と。私のコードに足りないのは、ドメインの概念を正しく切り分ける目だ。仕訳とは何か。請求とは何か。それぞれがどこで交わり、どこで分かれるのか。答えが見えた気がした。

意気込んでエディタを開いた。

指はキーボードの上で止まった。

何も書けなかった。

本で読んだ知識は、コードの前では沈黙した。

技術書を読んで「なるほど」と思うことと、自分の手元のコードを正しく切り分けられることのあいだには、気の遠くなるような距離がある。厄介なのは、「わかった」と感じた瞬間に、その距離が見えなくなることだ。分かった気になると、書けない自分を直視しなくて済む。

ドメインを知らずに境界は引けない

たかが趣味の会計ソフトだ、と思うだろうか。

私もそう思っていた。しかし「仕訳を記録する」というたった一つの動作の裏には、思った以上の文脈が折り畳まれていた。勘定科目の分類ルールは売上と経費で異なる。消費税率の判定。請求と入金のタイミングのずれ。外部の銀行明細との突合。そして確定申告に向けた帳簿の整形。

むしろ趣味だからこそ、全部ひとりで書いていた。設計もテストもデバッグも、全部自分。誰にもレビューしてもらえない代わりに、すべての痛みが自分に返ってくる。

最初は「ざっくり動けばいい」と思っていた。しかし「ざっくり」を支えるには、「きっちり」の構造を知らなければならない。どこを省略してよいかは、全体を理解していないと判断できない。

頭では分かる。しかし、コードに落とそうとすると、境界が溶ける。消費税の計算が仕訳の作成メソッドの中に滲み出す。外部APIの呼び出しが、ドメインロジックと同じレイヤーに住み着く。気がつくと、また密結合なコードが出来上がっている。

ドメインを理解していなければ、疎結合なコードは書けない。

Evansはもう一つ、大事なことを言っていた。ナレッジクランチング——知識の咀嚼。開発者がドメインエキスパートと繰り返し対話し、モデルを練り上げていく過程のことだ。モデルは最初から正しくない。何度も作り直す。つまり、最初から正しい境界線は引けない。それが前提なのだ。

そして私は、まさにその「最初から正しくないモデル」を書いている真っ最中だった。

密結合が教えてくれたこと

では、ドメインを理解していなかった頃に書いた密結合なコードは、失敗だったのか。

いや。あれは必要な過程だった。

密結合なコードを書いたからこそ、どこが絡まっているのかが見えた。請求と仕訳が不自然に結びついている箇所を、身をもって知った。消費税率が変わったとき、仕訳テーブルのどこを直せばいいのか分からなくなった。ひとつの修正が、思いもよらない場所のテストを壊した。影響範囲が読めない恐怖を、身をもって知った。

あの密結合を経なければ、疎結合の「正しさ」は、教科書の中の言葉にとどまっていただろう。少なくとも私は、痛い目を見ないと本当には分からなかった。

間違ったコードを書く時間は、無駄ではない。

だから、今まさに密結合なコードの前で途方に暮れている人がいたら、こう伝えたい。そのコードは失敗じゃない。痛みを感じているなら、それはもう境界が見え始めている証拠だと思う。

境界が見えたとき

あれから数ヶ月が経った。

あの会計ソフトは、まだ完成していない。誰に見せるわけでもない。ただ、自分の手でドメインの境界を引いてみたくて、書き続けている。

それでいい、と思っている。

転機は、消費税区分の判定だった。仕訳の作成処理の中に埋もれていたそのロジックを、試しに独立したモジュールとして切り出してみた。最初は不安だった。切り出して大丈夫なのか。呼び出し元が増えるだけじゃないのか。しかし切り出した瞬間、気づいた。消費税の判定は、仕訳とは別の関心事だったのだ。仕訳は「何が起きたか」を記録する。消費税の判定は「その取引にどんな税区分が適用されるか」を決める。目的が違う。だから、分かれるべきだった。

同じように、請求と入金のステータス管理を、仕訳のライフサイクルから引き剥がした。

分けてみると、それぞれが驚くほど小さくなった。小さくなったモジュールは、読める。読めると、変更できる。変更しても、他が壊れない。

疎結合とは、技術的なテクニックではなかった。ドメインの中にある概念の境界を見つけ、それをコードの構造に写し取ること。境界が正しければ、結合は自然と疎になる。逆に言えば、境界が見えていないうちは、どんなにインターフェースを切っても、抽象化を重ねても、コードは密に絡まっていく。

仕訳はなぜ仕訳として独立しているのか。消費税の判定はなぜ記録の作成と別の関心事なのか。その「なぜ」に自分の言葉で答えられたとき、コードの境界は自然と見えてくる。Evansの言葉を借りるなら、私はようやく一つだけ、暗黙の概念を明示的にできたのだと思う。一つだけ。まだ他にもきっとある。

あなたのコードの中に、まだ名前のついていない概念はないだろうか。

痛みの先に見えるもの

この記事で言いたかったことは、結局、1つだけだ。

正しい境界は、間違ったコードの先にしか見えてこない。

本を読めば概念は分かる。インターフェースの切り方も、レイヤーの分け方も、教科書に書いてある。だが、自分のドメインの境界線——それだけは、自分の手で間違えてみなければ見えない。

私はまだ、ほんの一つか二つの境界を見つけられた程度にすぎない。次にまた別のドメインで同じ壁にぶつかるだろう。また密結合なコードを書くだろう。それでも、あの深夜に感じた「何かが違うのに言葉にできない」という苦しさの正体を、今の私は知っている。

あれは、境界が見える直前の痛みだった。

だから——と、これは自分に言い聞かせているのだが——本を閉じて、エディタを開こう。密結合を恐れずに書こう。正しいかどうかは、書いた後にしか分からない。

——誰に見せるわけでもない会計ソフトを、今夜もまた少しだけ書き直す。まだ見えていない境界が、きっとある。


参考書籍 エリック・エヴァンスのドメイン駆動設計 ソフトウェアの核心にある複雑さに立ち向かう(Eric Evans 著、今関剛 監訳、和智右桂・牧野祐子 訳、翔泳社、2011年)

※本記事におけるEvansの主張についての記述は、筆者の理解に基づく要約であり、原著からの正確な引用ではありません。