🐙6回引っ越した話と、変化に強いシステムの設計
2020年から2025年にかけて、5年間で6回引っ越した。
1回目は銀行の寮だった。新卒入社直後で、荷物はスーツと卒業証書が数枚くらいのもの。段ボール数箱で完結した、人生で一番ラクな引っ越しだった。
2回目から様相が変わった。1年ちょっと生活すれば、家電が揃い、本が積まれ、気づいたら軽自動車にパンパンになるくらいの荷物ができていた。
3回目はさらに増えた。ハイエースを1台借りて、一往復。「あ、自分の荷物ってこんなにあるんだ」と実感した回だ。
4回目は覚悟を決めて断捨離をした。それでもハイエースに詰めるくらいの量はあって、特に冷蔵庫の扱いに苦労した記憶がある。縦に倒せない、傷つけたくない、運ぶ先が遠い。あれは大変だった。
引っ越しが大変な理由をまとめると、こうなる。
- 運ぶ量が多いほど大変
- 運び先が遠いほど大変
- 雨に濡れたらまずいものがあると尚更大変
- なるべく回数は少ない方が楽
- なるべく一度に運ぶ量を減らす方が楽
これ、ソフトウェアの移行戦略とほとんど同じ話だと思った。
オライリーの「進化的アーキテクチャ」を読んでいると、引っ越しの比喩がよく合うことに気づく。本の副題は「絶え間ない変化を支える」。組織も、アーキテクチャも、データモデルも、変化を前提として設計しなければならない、という話だ。
以下、組織戦略・アーキテクチャ戦略・データモデリング・移行戦略の順に、引っ越しの比喩を使いながら整理する。
組織戦略:引っ越しのやり方はチームで決まる(Conway’s Law)
1968年、コンピュータ科学者の Melvin Conway はこう述べた。
「システムを設計する組織は、その組織のコミュニケーション構造をコピーしたシステムを生成する」
引っ越しで言うと、「荷物の分け方はチームの分け方で決まる」だ。自分と親が一緒に暮らしていれば、荷物は一緒にまとめて運ぶ。別々に住むなら、別々の段ボールに分けて運ぶ。どこで区切るかは、一緒に動く人の単位で決まる。
ソフトウェアも同じだ。フロントエンドチームとバックエンドチームが分かれていれば、自然と API の境界でシステムが割れる。注文・在庫・決済でチームが分かれていれば、自然とその境界でサービスが分割される。チームの分割線 = システムの分割線になる。
これは観察であって、呪いではない。逆手に取ることができる。
逆コンウェイ戦略(Inverse Conway Maneuver) とは、「理想のアーキテクチャに合わせてチームを作る」という戦略だ。先にシステムをどう分割したいかを決め、そのシステムを自律して動かせるチームを編成する。Amazon が 2 ピザチーム(6〜10人)でマイクロサービスを作ったのはこの考え方だ。
「マイクロサービスに分割したのに、なぜかリリースするたびに全チームの調整が必要になる」という状況は、アーキテクチャとチームの分割線がずれているサインだ。先にチームの境界を見直す必要がある。
アーキテクチャ戦略:濡れたらまずいものを先に包む
引っ越しで最初にやることは、壊れやすいものや濡れたらまずいものを先に丁寧に梱包することだ。書類、パソコン、写真。これらは雑に扱えない。逆に、服や本はある程度雑に詰めても大丈夫だ。
ソフトウェアでは「濡れたらまずいもの」 = コアドメインだ。注文ロジック、課金ルール、在庫計算。これらが壊れると、ビジネスが壊れる。
「進化的アーキテクチャ」が提唱するのが Fitness Function(適応度関数) という概念だ。アーキテクチャが守るべき特性(パフォーマンス・結合度・セキュリティなど)を自動検証する仕組みで、CI に組み込んで変更のたびに動かす。
テストとの違いは検証対象だ。
| テスト | Fitness Function | |
|---|---|---|
| 守るもの | 機能の正しさ | アーキテクチャの特性 |
| 例 | 「注文の合計金額が正しいか」 | 「循環依存が存在しないか」「P99が200ms以内か」 |
テストが「パソコンが動くか」を確認するなら、Fitness Function は「ちゃんと梱包されているか」を確認する係だ。
そして Incremental Change(段階的変更) が「一度に全部運ばない」に対応する。大きなリリースは変更範囲が広く、問題が起きたときの原因特定が難しい。小さく変えることで、失敗を早く検知して素早く修正できる。Feature Toggle(機能フラグ)で「新機能のスイッチ」を持ち、Canary Release で 1% → 10% → 100% と段階的にトラフィックを切り替える。
もう一つ、Appropriate Coupling(適切な結合)。「疎結合を目指せ」という言葉はよく聞くが、疎結合が常に正しいわけではない。過剰に分割すると、引っ越しの荷物を細かくしすぎて「どこに何があるかわからない」状態になる。モジュールを細かく分割しすぎると、ネットワーク越しの呼び出しが増え、障害点が増え、整合性を保つのが難しくなる——「分散モノリス」と呼ばれる最悪の構成だ。どこで区切るかを、意識的に設計する。
データモデリング:新居と旧居が同時に存在する期間
引っ越しには必ず「新居と旧居が同時に存在する期間」がある。新居の鍵をもらった日から、旧居の鍵を返す日まで。この期間をうまく使えると、引っ越しはずっと楽になる。少しずつ荷物を移せるし、新居で生活しながら「ここに収納がほしい」と気づいてから家具を買える。
データベースのスキーマ変更も同じだ。カラム名を変えたい、型を変えたい、テーブルを分割したい。でも本番DBを止めて一気に変更すると、ダウンタイムが発生する。古いコードが壊れる。
Expand-Contract パターンはこれを3フェーズで解決する。
Expand(拡張):新居を契約するフェーズ。新しいカラムを追加し(NULL許容)、アプリは新旧両方のカラムへ書き始める。旧カラムも現役のままで、システムはどちらかが欠けても動く。
Migrate(移行):荷物を移すフェーズ。古いレコードを新カラムへバックフィルする。1000件ずつバッチで動かして本番負荷を抑える。この期間、一時的な Fitness Function(整合性チェック)を CI に仕込んで、旧カラムと新カラムの値が一致しているかを自動検証する。
Contract(縮小):旧居を解約するフェーズ。読みを新カラムに切り替え、旧カラムを削除する。ここが一番不可逆なので、十分な観察期間を設ける。
「新居に慣れてから旧居を手放す」のが安全だ。Contract フェーズの旧カラム削除だけは、急がない。
移行戦略:どうやって荷物を運ぶか
マイクロサービスに分割するとき、旧ストアから新ストアに移行するとき、「データをどうやって運ぶか」の選択肢は3つある。引っ越し業者の種類に例えて整理する。
Dual Write:自力で両手に持って運ぶ
アプリが同じデータを 2 つのストアに書き込む。コードは単純で、追加の仕組みが不要。引っ越しで言えば「自分で荷物を両手に持って新居まで歩く」だ。
問題は片手を落としたら終わりなことだ。旧ストアへの書き込みが成功し、新ストアへの書き込みが失敗した場合、2つのストアに不整合が生じる。このエラーはサイレントで、気づいたときには大量のデータがずれている。
移行期間が短く、整合性の要件が緩い場合に適している。
Outbox Pattern:荷物リストを業者に渡す
引っ越し業者への依頼で一番大切なのは「荷物リスト」だ。リストに載せた荷物は、業者が必ず届けてくれる。
Outbox Pattern は「DB への書き込み」と「送るべきメッセージの記録(outboxテーブルへの INSERT)」を同一トランザクションで行う。別プロセス(Outbox Poller)がこのリストを読んで、メッセージキューへ発行する。
DB と outbox への書き込みが同一 TX に収まっているため、「DB には保存されたがメッセージは届かなかった」という部分失敗が起きない。リストに載せた荷物は必ず届く。コストは「業者手配」分——outbox テーブルとポーラーの実装が必要だ。
CDC(Change Data Capture):全自動引っ越しサービス
荷物を一切触らずに、プロが全部やってくれる。Debezium のような CDC ツールは、DB の変更ログ(WAL: Write-Ahead Log)を読んで、変更を自動的に下流システムへ伝播する。アプリコードを一行も変えずに、旧ストアから新ストアへのデータ同期が実現できる。
代わりに、一番コストが高い。Kafka と Debezium の運用が必要で、小規模なプロジェクトには見合わないことも多い。
| 手法 | 難易度 | 信頼性 | 適用場面 |
|---|---|---|---|
| Dual Write | 低い | 部分失敗リスクあり | 小規模・短期移行 |
| Outbox Pattern | 中 | 高い(同一TX) | イベント駆動・整合性重視 |
| CDC | 高い(運用含む) | 高い | 大規模・長期・コード変更不可 |
まとめ:断捨離した引っ越しが一番楽だった
4回目の引っ越しで断捨離をしたあと、荷物の量が減った。そのあとの引っ越しが、断然楽になった。冷蔵庫の大変さは残ったが、「移動する総量」が減ったことで全体のコストが下がった。
ソフトウェアも同じだ。変えやすい構造を最初から作っておけば、次の「引っ越し」のコストが下がる。
「進化的アーキテクチャ」が問うのは「変化に耐えるか」ではなく、「変化を前提に設計するか」 だ。Conway’s Law を逆用してチームを組み、Fitness Function でコアを守り、Incremental Change で小さく動かし、Expand-Contract で安全にスキーマを移行する。これらは全部、「次の引っ越しを楽にするための設計判断」だ。
引っ越しは、した回数だけうまくなる。でも、最初から「荷物を増やさない」「捨てやすくしておく」設計ができていれば、もっと楽だったはずだ。ソフトウェアはやり直せる。設計を見直すなら、早いほど安い。
参考文献
- Neal Ford, Rebecca Parsons, Patrick Kua, 進化的アーキテクチャ 絶え間ない変化を支える(O’Reilly)
- → 進化的アーキテクチャとは:定義・誕生背景・3原則
- → Expand-Contract パターン
- → Dual Write の仕組みと落とし穴
- → Outbox Pattern
- → CDC(Change Data Capture)
- → Conway’s Law と組織戦略