2018年6月にTalenteraで出会ったスタックを説明しよう:MENAリージョン全体の政府省庁やエンタープライズクライアントにサービスを提供するマルチテナントのB2B採用SaaSで、AOL Server 4.x上のTCL/TKコアで動き、新しい機能にはPHP 7、一部のAPIレイヤーにNode.js、ワークフローオーケストレーションにCamunda BPMNエンジンを備えていた。これはプロトタイプではなかった。これは本番だった。実際の政府のために実際の採用決定を処理していた。そして2018年、主の年に、TCL 8.4と8.6だった。
何を考えているかわかる。止めてほしい。
TCL/TKはジョークではない
本番スタックでTCLを見たエンジニアの最初の反応はたいてい「まだ移行していないんだね」だ。そのフレーミングは間違っていて、悪い決定につながる。
TCL — Tool Command Language — は1988年に生まれ、まさに一つのことのために設計された:スクリプティングと異質なシステムのグルーイングだ。TalenteraのウェブランタイムだったAOL Serverは、ネイティブ拡張言語としてTCLを使用している。AOL Server自体は本当に印象的なインフラだ:イベント駆動、マルチスレッド(スレッドプールあり、リクエストごとに一スレッドではない)、組み込みの効率的なコネクションプーリング。元のNaviServerフォークは今でもアクティブに開発されている。なぜならモデルが実際に機能するからだ。Googleのウェブインフラチームが2000年代初頭にファーストプリンシプル作業をしていたとき、AOL Serverのアーキテクチャは肯定的に引用された。これは劣化によるレガシーではない — 長寿によるレガシーで、それは別物だ。
2018年に新鮮に始めるなら選ばないバージョン番号だが、ランタイムが安定していることを意味するバージョン番号だ。破壊的変更なし。驚きの非推奨なし。到着前に書かれたすべてのTCLコードはまだ書かれた通りに動く。何千人もの候補者のために採用ラウンドを実行している政府でダウンタイムが直接影響するマルチテナントSaaSでは、退屈な安定性は競争上の優位性だ。
言語自体 — Tclは独特の美しさを持っている。すべてが文字列だ。コマンドはただのリストだ。実行モデルは著しくコンポーザブルだ。Talenteraがやっていた種類のリクエスト処理 — クエリを形成し、レスポンスを組み立て、タイトなサーバーサイドループで構造化データを操作する — には正直言って問題ない。素晴らしくはないが問題ない。「問題ない、安定している、そしてすでに書かれている」は、エンタープライズSLAを維持する必要があるとき、「より良いがリライトが必要」にほぼ常に勝る。
「レガシースタック」が実際に何を犠牲にするか
正直な数字はこうだ:レガシースタックの実際のコストはランタイムではない。オペレーショナルサーフェスだ。
AOL Server + TCLにはリッチなモダン可観測性ストーリーがない。TCLネームスペースが2003年代のサーバーモデルのスレッドプール内で動いているコアを持つときに分散トレーシングを追加するには、Go、Java、Node.jsでオープンソースエコシステムがすでに解決した問題を解く必要がある — しかしこれのためには解かれていない。それは機能に向かわないエンジニアリング時間だ。私が参加したとき、チームは「次にTCLが何をすべきか」に比べて「本番でTCLが何をしているかを可視化するにはどうするか」に不釣り合いに多くの認知オーバーヘッドを費やしていた。
それが実際に解くべき問題だ。「TCLをリライトする」ではなく — それは会社を潰すかもしれない複数年のプロジェクトだ — しかし「TCLを操作する認知オーバーヘッドを減らして、チームが次にプロダクトを構築することに集中できるようにする」。
私たちが辿り着いた答え:TCLコアを移行しない。代わりに、それを孤立させる。
DDD + マイクロサービスをラッパーとして、置き換えとしてではなく
私がリードしたアーキテクチャアプローチは、TCLコアの代わりではなく、その周りにレイヤー化されたドメイン駆動設計だった。Camunda BPMNは、純粋なTCLで管理するには複雑になりすぎたプロセスのワークフローオーケストレーターになった — 多段階の承認チェーン、異なる政府クライアント設定のための条件分岐、ドキュメント処理パイプライン。CamundaはJavaを話す。つまり、ワークフロー定義レイヤーを所有するJava 8サービスで、TCLが特定のタスク実装を実行するワーカータイプの一つとして機能した。
KeycloakはマルチテナントランドスケープにわたるIdAM — OAuth2とOpenID Connect — のために入った。2018年の正しい判断だった。Keycloak以前、テナントIDはアーティザナルな方法で処理されていた。カスタムセッショントークン、リクエストハンドラー全体に散らばったテナントごとの認証ロジック、限られたフェデレーションサポート。Keycloakはそれを集中化し、標準準拠の認証を提供し、新しいサービス(KotlinマイクロサービスとNode.js APIレイヤー)がTCLセッション内部を理解する必要なしに同じIDモデルに参加できるようにした。
RabbitMQが非同期メッセージングを担当した。Redisがキャッシングを担当した。新しいモバイル機能 — Ionicとその後Flutter — は、モバイルクライアントの期待とTCLベースのバックエンドの現実の間で翻訳するNode.js APIゲートウェイに話しかけた。これのどれもTCLコアに触れる必要がなかった。TCLコアは動き続けた。新しいサービスはその周りに川の周りに育つ都市のように育った。
KotlinとTCLを同じスプリントで出荷した週
TCL 8.4とKotlinの間でコンテキストスイッチする特定の認知的な衝撃がある。TCLでは、文字列操作とネームスペース管理と引数リストが正しいことの確認について考えている。Kotlinでは、コルーチンとデータクラスとNull安全性について考えている。パラダイムはあまりにも異なってほぼ直交している。
私が発見したこと、そして驚いたことは、これが実際には問題ないということだ。アーキテクチャが強制した懸念の厳密な分離 — TCLがリクエスト処理レイヤーを所有し、Kotlinが新しいドメインサービスを所有し、Camundaがプロセス定義を所有する — はコンテキストスイッチがクリーンだったことを意味した。TCLにいるときはTCLの問題について考えていた。Kotlinにいるときはkotlinの問題について考えていた。コンテキストは境界が本物だったからにじみ出なかった。
これは明示的に境界化されたアーキテクチャの過小評価された議論の一つだ:マルチテクノロジーショップを操作的に扱いやすくする。モノリスを構築して新しい言語を導入しようとしているなら、どこでもboundary mixが起きる — PHPはどこで終わってNode.jsはどこから始まるか?DDDをきちんとやったとき、境界はただのネームスペースではなくデプロイ境界だ。その明確さは、同じスプリントで30年前のスクリプト言語とモダンなJVM言語を切り替えているチームには重要だ。
移行が実際にどんな様子か
Talenteraでの時間の終わりに、完成していなかったとしても移行パスが明確に見えていた。答えは「TCLをリライトする」ではない。答えはこうだ:
- TCLの実装が実際に問題を引き起こしているドメインを特定する。「これは古い」ではなく「これは積極的に私たちを制限している」 — より豊かな型システムや限られた可観測性ストーリーの欠如が実際のエンジニアリング時間を犠牲にしている場所。
- そのドメインのための新しいサービス境界を定義する。新しいサービスでそのドメインのデータを所有する。
- Camundaに移行を仲介させる — ワークフローエンジンは新しいサービスがオンラインになるにつれてタスクを新しいサービスにルーティングでき、自信が持てるまでTCLの実装が同じタスクを並行して処理する。
- そのドメインのTCLの実装のトラフィックをドレインしたら、廃止する。
それは年単位ではなく四半期単位で測られる移行タイムラインだ。そして一度にすべてをリライトする必要がない。重要なインサイトは「移行」は「置き換え」ではない — 「所有権の体系的な再配置」だということだ。TCLは消える必要はない。ただ、得意ではないドメインを所有することをやめる必要がある。
レガシースタックを引き継ぐことについて誰も言わないこと
このようなスタックを動かす会社に参加するとき、完全なリライトを提案することで自分の印を残したいという誘惑がある。その誘惑に抵抗することは最も重要なアーキテクチャ上の決定の一つだ。本番のマルチテナントSaaSの完全なリライトは会社を賭けたプロジェクトだ。スケジュール通りにほぼ決して終わらない。新しいものは古いものが処理していたエッジケースをほぼ決して複製しない。そしてリライト中、新しいプロダクトを構築していない — 既存のプロダクトの置き換えを構築している。競合は立ち止まっていない。
より価値のあるスキルは、スタックを慈悲深く読むことを学ぶことだ。AOL Server上のTCLは古いから技術的負債ではない。そのオペレーショナルモデルが本当の摩擦を生み出す特定の場所での技術的負債だ。それらの場所がどこかを理解しろ。それらの場所を具体的に修正しろ。残りはほっておけ。
その環境でKotlinマイクロサービスを出荷した。BPMNワークフローオーケストレーションを出荷した。テナント全体のOAuth2フェデレーションIDを出荷した。TCLコアはずっと動き続けた。
それが仕事だ。