LegalOn Technologiesが提供する「LegalOn: World Leading Legal AI」は、契約書ドラフト・レビューや案件の管理、法務相談まで、法務業務をワンストップで支援する革新的なサービスです。リリースから1年半、企画開始から約2年半が経った今、プロジェクト立ち上げの背景、開発の進め方、これまでの振り返りや今後の展望、さらには技術面での学びまでを、全9回にわたるブログシリーズとしてお届けしていきます。
第7回となる本記事では、「LegalOn」のバックエンドアーキテクチャと技術選定に焦点を当て、当時の意思決定の背景や、これまでの成果・課題について、CTOオフィスリーダーの時武が以下の3名に話を聞きました。
- 北林 巧巳:Staff Engineer
- 松本 翔:Senior Engineer
- 宮腰 茂明 :Senior Engineer
マイクロサービスにおける共通基盤の構築
時武 まずは、リリース前にどのような技術的な意思決定がなされたのか、特に良かったと感じる点について聞きたいです。モノレポ・マイクロサービスという全体像は、第4回の全体アーキテクチャ設計と技術選定編でも話がありましたが、バックエンドとして数多くのサービスがある中で、実際にどうだったでしょうか。
松本 個人的に正解だったと感じているのは、認可機能の設計ですね。認可ルールをProtocol Buffersのカスタムオプションとして記述するアプローチは良かったと感じています。アプリケーションの中に認可ロジックが埋もれず、API仕様の一部として明確に記述できた点は大きかったと思います。
時武 あれはたしか松本さんの発案でしたっけ?何か参考にした事例はありました?
松本 特に具体的な事例があったわけではなくて、思いつきで調べてみたら、似たような取り組みをしている例がいくつか見つかった、という感じでした。直感的に「良さそう」と思ったんです。
この方法のおかげで、アプリケーションコードを書く量をかなり抑えることができました。認可はバグが許されない領域なので、認可ロジックの一覧性や網羅性が高まったのは安心材料でしたし、チーム間の実装のブレを防ぐガードレールとしても機能したと思います。
時武 なるほど。北林さんはどうですか?
北林 私はNotificationサービスを担当していました。大量のメール通知が一斉に送られる可能性があるので、詰まりが起きないように設計を工夫しました。特に「これが決定打だった」という技術的正解はないかもしれませんが、共通のNotificationサービスを作ったことで得られたメリットは大きかったと思います。外部サービスとの接点をひとつにまとめられたことで、トラブルがあったときの対応が本当に楽になりました。
松本 実際、使う側から見ても便利でした。SendGridのレートリミットとか、いちいち気にせずNotificationサービスに任せられるのはすごく助かりました。
時武 マイクロサービスが多い分、共通機能の設計って本当に重要ですよね。特に、初期の段階で整備しておくことが後々効いてくる。
北林 そうですね。共通機能が他チームから利用され始める前に仕上げておかないといけなかったので、スケジュールはかなりタイトでしたね。
時武 共通機能って、プロジェクトのクリティカルパスに入りやすいので、スケジュール管理の難易度も高いですよね。
宮腰さんは、「これは正解だった」と思える点はありますか?
宮腰 共通テンプレートの存在が大きかったです。JavaとGoの両方にテンプレートが用意されていたことで、非同期タスクなどの共通処理を実装する際に、全体として統一感が出せました。テンプレートがなかったら、各チームがバラバラな実装をして制御しきれなくなっていたと思います。
もちろん、テンプレートから外れた部分ではチームごとの工夫もありましたが、基本構造が共通化されていたおかげで、全体の整合性は保たれていたと思います。大規模な開発では、どこまで共通化し、どこに裁量を持たせるかというバランスが本当に大事ですよね。

アーキテクチャと設計思想の浸透
時武 では逆に、徐々に見えてきた課題はありますか?
宮腰 振り返ると、共通テンプレートで明確にされていない部分については統一感がない実装になっている点が課題です。具体的にはレイヤーごとの責務が明確に定義されていなかったり、チームごとに認識がずれていたこともあって、実装の仕方がバラバラになってしまった印象です。
時武 今回、GoとJavaの両方の言語を使っていましたが、バックエンドのレイヤー構造や実装において、言語による違いはありましたか?
宮腰 多少の違いはありましたが、それよりも全体として「どういうレイヤー構造にするか」という基本的な共通認識が足りていなかったかな、と思います。早い段階で設計思想をテンプレートに反映するか、あるいは事例共通の場を設けて方向性を揃えておくべきだったかもしれません。
松本 たしかに。より「Opinionated(思想が堅い)」なフレームワークや基盤があれば、サービス間の実装のばらつきはもう少し抑えられたかもしれませんね。たとえば、Railsのように“作法”がある程度決まっているものを複数のサービスで使えば、基本的な設計の共通化がしやすくなります。今回の開発では、アーキテクチャの自由度が高かったぶん、ばらつきも出やすかったのかな、と思いました。
時武 なるほどー。バックエンド開発におけるレイヤードアーキテクチャや、DDD(ドメイン駆動設計)の考え方は、どれくらい浸透していました?
松本 少なくともGoに関しては、当初レイヤードアーキテクチャの考え方がしっかり共有されておらず、サービスによって構成が異なっていました。当初はGoのテンプレート自体が、レイヤードアーキテクチャを意識して作られていなかったという事情もありますね。
北林 それは私も感じていました。テンプレートが網羅していない部分については、各チームが独自に作っていくケースが多かったですよね。
宮腰 レイヤーという観点では意識されていたものの、DDDの中でも特にドメインモデルについては、ほとんど実践されていなかったと思います。多くのチームで、ドメインモデルが単なるDTO(Data Transfer Object)になってしまっていて、本来持たせるべきビジネスロジックを持たせられていなかったのが課題です。
時武 その課題感については私もよく聞きますね。ドメインモデルがDTO化していると、将来的に開発スピードや共通認識の形成に影響が出てきそうです。
宮腰 その通りで、ドメインモデルにどんな操作を持たせるかという設計は本当に難しいんです。正直なところ、大規模プロジェクトでは厳密なDDDを適用するよりも、DTOで割り切って実装する方が現実的な場面もあります。ドメインモデルをしっかり運用していくには、チーム全体の理解やリテラシーが求められるので、規模が大きいほどその難しさは増しますよね。
大規模開発特有のハードル
時武 次に、開発する中で、これは大変だったという場面や、困難を乗り越えたエピソードがあればぜひ教えてください。
松本 個人的に一番大変だったのは、GoとJavaの両方で基盤機能ライブラリを実装しなければならなかった点ですかね。マイクロサービスアーキテクチャでは、サービスごとに異なる言語を使ってもいい、というのが建前としてありますが、実際に共通ライブラリを2言語分メンテするのは想像以上に骨が折れました(笑)。
さらに、機能を細かく分けすぎるとサービスの数が増えて、メンテナンスの負担や属人化のリスクも高まるんですよね。あらためて分割のバランスがすごく重要だなと実感しました。
北林 私はマイクロサービス開発の初期段階、特に開発環境の立ち上げで苦労しました。一部のサービスは動くけど他のサービスはまだできていない、あるいはサービス間の通信がうまくいかない、といった状態が続いて、きちんと動かせるようになるまでに数週間かかったこともありました。モックで代替するケースもありましたが、できるだけ本物を使いたいという声もあって。各チームの進捗状況や、モックを使うかどうかの判断が異なっていたこともあって、足並みを揃えて開発を進める難しさがありました。
宮腰 私はむしろ、技術的な挑戦が多くて楽しかった記憶の方が強いですね(笑)。とはいえ、特定の領域ではかなり人数を投入して開発を進めたにも関わらず、データモデルがどんどん複雑になってしまい、これは今も課題として残っています。
時武 確かに、かなりのリソースをかけて進めたのに、期待通りのアウトプットが出てこない場面もあり、プロジェクト進行の難しさを感じました。大規模な並行開発だと、最初に完璧な設計をするのはほぼ不可能で、走りながら微調整を重ねていく必要があります。開発の途中でこまめに軌道修正していくことの大切さをあらためて痛感しました。
時武 ここまでの振り返りを踏まえて、もし何か後悔している点があればお聞かせください。
松本 実はあまり後悔はないんですよね。もちろん大変だった部分は多々ありますけど、致命的に何か失敗をしたと感じるようなことはなかったかな。
北林 内部向けの一部のツールでは、一時的にローコードツールを導入したのですが、これは反省点として残っています。当時は開発リソースや時間の制約もあり、やむを得ない判断ではあったのですが、最終的には再開発が必要になってしまって。
導入時の調査が不十分だったかもしれませんし、ローコードのバージョン管理も難しさもありました。選択肢が少なかったこともありますが、ノーコード/ローコードでやる前提で進んでしまったのは反省点でした。
宮腰 個人的には、BFF(Backend for Frontend)を最初から採用していれば良かった、というのが一番の後悔ですね。今の構成だと、複数のマイクロサービス間でデータを連携させる必要があり、トランザクション的な処理がとても書きづらい状況になっています。もっとデータサービスをモノリシックにまとめて、BFF側でフロントとの連携を担う構成にしていれば、全体の複雑性を抑えられたのではないかなと思います。
時武 それは前回のフロントエンド編でも話に上がっていましたね。当時は、社内にBFFの知見があまりなかったことや、開発工数が増えることへの懸念もあったかもしれません。
北林 今後のAPI設計では、BFFの考え方はかなり有効になってくると思います。
松本 自分も同意です!たとえば共通機能ライブラリも、BFF経由で提供していれば、もっときれいに整理できたかもしれません。
時武 最近CTOオフィスではAIエージェントを使った開発の支援をしていますが、もし「LegalOn」にBFFがあったら、AIの導入ももっとスムーズになっただろうなと感じています。当時は状況的にやむを得ない部分もありましたが、間違いなく今後に活かせる教訓ですね。
持続可能なシステムへの進化と組織学習
時武 運用フェーズに入ってからの気づきや、良かった点について教えてください。
松本 開発担当者にとっては大変だったと思いますが、認可の仕組みがビジネス要件の変化に合わせて進化し続けている点は、すごく評価できると思います。
特にプラン体系の変更に伴って認可の要件が大きく変化した時に、単なる修正ではなく、本質をとらえた設計のブラッシュアップが行われたのは印象的でした。最初から完璧な設計を目指すのではなく、実際に使ってみて課題を見つけて改善していく、そうしたアプローチがうまく機能したと感じています。
時武 なるほど。あれだけの複雑なアプリケーションで、結果的にオーバーエンジニアリングを回避できたというのも、ひとつの成功と言えるかもしれませんね。
北林 あと、「LegalOn」の設計や開発プロセスが、今まさに進行中の他の製品開発にも活かされている点は、大きな成果だと思います。最初の設計がしっかりしていたからこそ、他のチームがそれを参考にしながら、良い部分を取り入れたり、課題を改善したりできている。
こういう学びが循環している状態は、組織全体にとってすごく価値があると思いますし、大規模開発の経験が組織の資産になっていると感じます。運用フェーズでは、技術的なドキュメントに加えて、運用のための知見も体系化されてきていて、その土台が「LegalOn」で作れたのは大きいです。
時武 「LegalOn」がレガシー(組織の財産)となって、良いところを他のプロダクトが引き継いでいく流れができているのは、本当に素晴らしいことですよね。
宮腰 それから、運用フェーズで大規模な障害やセキュリティインシデントが発生していないことも伝えたいですね。サービスが何日も使えないような事態が起こらなかった。これは、リリース前から堅牢な仕組みやフェイルオーバーの仕組みが整っていたことによるものだと思っています。リリース直前は「大丈夫かな」と不安もありましたが、大きな問題が起こらずほっとしています。
時武 これだけの規模のサービスで、致命的な問題が発生していないのは、しっかりとした設計のおかげですね。リリース前は皆さん不安もあったと思いますが、結果として堅実な設計が功を奏したと思います!
LegalOn Technologies では、「Product Centric(全員がプロダクトの価値向上のために活動する)」 を大切にし、学ぶことに前向きで、挑戦を楽しめるエンジニアを募集しています。今回の記事で触れたような技術的な挑戦や、チームで課題を解決していくプロセスに興味を持たれた方は、ぜひ以下の採用サイトをご覧ください。皆さんのご応募をお待ちしております!
recruit.legalontech.jp
herp.careers
次回は、「第8回「LegalOn」誕生の裏側:検索編」をお届けいたします。
コメント