株式会社ヘンリー エンジニアブログ

株式会社ヘンリーのエンジニアが技術情報を発信します

医療スタートアップのバックエンドをモノレポ化した話 〜戦略・プロセス編〜

こんにちは、ヘンリーの Lead Architect の @kohii です。

弊社ではレセコン一体型クラウド電子カルテHenry を開発・提供しています。

最近 Henry のバックエンドをモノレポ化したので、その戦略やプロセスについて書きたいと思います。

こちらは前編となっており、モノレポ移行の手法やテクニックの話は後編で説明します。

dev.henry.jp

Why モノレポ?

ざっくり説明すると、既存のマイクロサービス/チームの分界点を抜本的に見直し、ドメイン(業務の領域)による分割を目指すため、一旦モノレポにまとめて、理想的な構造の切り出しをやりやすくするという目的です。

モノレポ化前のシステム/チームアーキテクチャ

バックエンド

Henryのバックエンドはマイクロサービスになっていますが、以下の2つのサービスが大部分を占めています。

  • henry-general-api電子カルテ「Henry」のバックエンド。基本的にはフロントエンドから呼び出される形で処理を行い、必要に応じて receipt-api を呼び出します。
  • henry-receipt-api … 「レセコン」と呼ばれる医事会計システムの領域を担当するサービス。ステートレスな計算エンジンとなっていて、general-api から呼び出され、診療報酬制度に乗っ取った処理を行い結果を返します。(ちなみに診療報酬制度はたぶんみなさんが想像するののだいたい50倍くらい膨大かつ複雑です。)

どちらも Kotlin で記述されていて、3年半に渡り50人の開発者が関わってきました。

マイクロサービス SLOC (Kotlin のみ) 累計コミット数 月間 PR マージ数
henry-general-api 13.9万 12,557 154
henry-receipt-api 14.6万 12,561 170

Henryの開発チーム

バックエンドの開発を行うのは以下の2チームでした。

  • Accomplishment Team … ユーザストーリーのデリバリーとインクリメントの最大化を追求する。general-api のオーナー。
  • Receipt Committee … レセプト機能を専門的に扱う。receipt-api のオーナー。

これら以外にも Platform Group(技術基盤の開発などを通じ組織全体の生産性を上げる)というチームもあります。

課題

プロダクトを作り始めて数年経ち、チーム/システムを分割した当初の意図と現状にギャップが生まれるようになってきました。

1. チーム構造とシステム構造の不一致

receipt-api は general-api のために存在する計算エンジンなので、Receipt Committee は Accomplishment Team から依頼を受けることを起点に receipt-api をインクリメントする想定でした。(Team Topologies で言うところの Complicated-subsystem Team)

しかし、実際には Receipt Committee はユーザー体験を含めた医事会計業務全般の開発や問い合わせ先を期待されることが多く、general-api やときにはフロントエンドの開発も担う必要がありました。(Team Topologies で言うところの Stream-aligned Team)

  • 開発フローやコミュニケーションパスの複雑化
  • 1つの機能のデリバリーがチームで完結せず時間がかかる
  • オーナーシップを持たないコードベースへ手を加えたり調査したりすることが多くある
    • 認知負荷の問題
    • 各チームの方針やコンテキストを理解しないコミットが入りやすい

2. チームの分界点の問題

チームの境界付近に非常に難しい問題が落ちていることが多く、そもそも分けてはいけない問題を分けているのでは?みたいな感覚がありました。

  • チームをまたがる開発の手戻りが多く、仕様確定までに時間がかかる
  • 仕様の認識齟齬が残ったままデリバリーされてしまう
  • チーム/システム間をまたいで相互に理解していないと適切なプロダクト設計にたどり着けない

3. 分散した密結合

API 呼び出しの方向は general-api → receipt-api なので、この方向の依存が発生するのは当たり前ですが、逆方向の依存も発生していました。具体的には「general-api は receipt-api を呼び出す時に、自身のドメインモデルをそのまま渡す」ということをやってしまっていたため、receipt-api は general-api のモデルを知っているということが起きていました。

この双方向の依存のため、コードベースやサービスは別れているのに、実質的には1つの密結合な塊として扱わねばならず、サービス境界面を含む変更容易性や理解容易性が損なわれ、アーキテクチャの改善そのものを難しくしていました。

戦略

これらの課題に対して、既存のアーキテクチャの延長線上でいくら捏ねくり回しても根本的な解決に近づくのは困難だと判断し、ゼロベースで理想のチーム・システムを考えました。そしてその理想を目指すトランジションの初手として、バックエンドのコードを一旦モノレポにまとめ、サービス境界の見直しを含めた変更をやりやすくするという戦略を取りました。

モノレポ化までの道

1. モノレポ化を決めるまで

開発体制再考ワーキンググループ発足

2023年2月頃に「チーム/システムの境界を跨ぐ機能に関して、開発がうまく進まない問題が頻発している」ということから「開発体制再考ワーキンググループ」が発足しました。(ヘンリーにはワーキンググループ(WG)という仕組みがあり、特定の課題を解決するために作られる一時的なプロジェクトのようなものが有志によって組成されます。)

その WG の中で議論を重ねた結果、既存のチーム構造を見直し、ドメインによるチーム分割を目指すということで方向性が決まりました。

  • 各チームは、担当する領域について顧客へ価値を提供するために必要なすべての機能/権限を持つ
  • 極力チームをまたがずに自律して意思決定し行動できる

これは「現状で正しそうに見える分割」であり、先に進む中で知見や洞察が深まり、徐々に実践的な正解が見いだされていくものだと思っています。

なお、実際には人数や役割の問題から、移行期として一旦は2チームに分け、それぞれ複数ドメインを受け持っています。

次にシステムアーキテクチャに関する議論が行われました。チームの構造とシステムの構造は一致しているべきということに異論はなく、そこにたどり着くまでのトランジションを議論しました。

いろいろなプランが挙がりましたが、結論としては既存のマイクロサービスを一旦モノレポにすることで理想の構造に移行しやすくするという方針に決めました。

  • 既存の構造の境界部分を含む変更がやりやすくなる
  • 正しいドメイン境界を特定するための試行錯誤がやりやすい

モノレポの具体像

まずは各サービスのデプロイメント単位は変えずに、コードベースを1つにするということを目指します。

Before:

  • henry-general-api (ルート)
  • henry-receipt-api (ルート)

After:

  • henry-backend (ルート)
    • general-api (Gradle のサブプロジェクト)
    • receipt-api (Gradle のサブプロジェクト)
    • utils (ユーティリティを共有。Gradle のサブプロジェクト)

これが終わった後は、モノレポの中でドメインによるコンポーネントの再構成を進める作戦です。

ADR (Architecture Decision Records) を記述

意思決定を ADR という形でまとめ、開発者間で共有しました。ADRアーキテクチャ決定を記述したもので、弊社の場合は Notion のデータベースに書いています。

どのようなアーキテクチャも様々な背景やトレードオフ、方針の上に成り立っていて、それをすべての開発者が理解することは重要です。また将来アーキテクチャに関する検討を行う時に、「なぜ今こうなっているのか」の経緯を後から遡って理解できるようにしておくという意味もあります。

2. モノレポ化の準備

モノレポ分科会の発足

「開発体制再考 WG」から、モノレポへのトランジションを実行する分科会が発足しました。

分科会では次のようなことをやりました。

  • Slack チャンネルを作成
    • 分科会のメンバー以外も任意で参加
  • スケジュールと計画の立案と周知
  • モノレポの具体像の決定
  • モノレポ化のためのバックログの作成・管理
  • 週次の定例ミーティングを設置
    • 週次のスプリントみたいな形で各々作業を進め、1週間後に結果や状況を確認し、また次週のプランを行う

作業日の決定とアナウンス

実施日は最も顧客業務への影響が少ない日曜日の夜間に決めました。

混乱なく進められるように、その週の初めごろに開発者向けのアナウンス Slack で周知しました。

手順書の作成

リスクを極力減らし、当日のコードフリーズ時間を最短にするため詳細な手順書を作成しました。可能な場合は具体的なコマンドまで記述しておきます。

3. モノレポ化当日

日曜日の17時から始めました。Google Meet で1人の作業者の画面を共有しながら行い、途中で夕食を食べたりしながら作業しました。一部想定通りに行かなかったところもありましたが、入念な準備のおかげで大凡滞りなくすべての作業を完了しました。

作業が完了した旨をアナウンスし、開発者が行うべき作業を伝えてこの日はおしまいです。

当日の技術的な内容は後編で説明してますので、よかったら読んでみてください。

dev.henry.jp

4. モノレポ化後初日

特に混乱なくモノレポ化後のコードベースで開発を始められました。

IDEのコードスタイルの設定漏れ等細かいものはありましたが、開発メンバーの協力によってすぐに解消されています。

やってみて

モノレポの Pros / Cons

モノレポ化はアーキテクチャの目的地ではなく、これからの変遷のための初手なので、これ自体に関する良し悪しはあまり本質的ではありませんが、だいたいこんな感じの感想です。

  • Pros:
    • general-api / receipt-api をまたがる変更をアトミックにできる
    • 設定やスクリプトの重複が排除され、一箇所で管理できるようになった
  • Cons:
    • Intelllij のサイドバーごちゃつきがち
    • general-api / receipt-api で同じ名前のクラスがあったため「今開いているこのファイルはどっちの持ち物のやつだっけ?」みたいな確認が必要なときがある

今後

具体的な次の一手としては、API の I/F の定義(proto ファイル)はまだモノレポの外に存在するので、これもモノレポに取り込みたいと考えています。これができれば general-api / receipt-api の境界面に関する変更がさらにやりやすくなるはずです。

一方でモノレポ化はドメインによるサービス分割を行うための一歩目であり、その方向に向かってアーキテクチャを移行していく旅は続きます。その先にあるゴールがマイクロサービスなのかモジュラーモノリスなのかは明確に見えているわけではありませんが、進んでみて、進んだ先にまた新たなことが見えてくる、の繰り返しだと思っています。


最後まで読んでいただきありがとうございました!

ヘンリーでは一緒に働く仲間を絶賛募集中です。興味がある方はぜひお気軽にご連絡ください。

jobs.henry-app.jp