Findy Tools
開発ツールのレビューサイト
検索結果がありません
目次
Xのツイートボタン
このエントリーをはてなブックマークに追加
Xのツイートボタン
このエントリーをはてなブックマークに追加
【アーキテクチャConference 2025】SaaS拡大期の成長痛 〜モジュラーモノリスへのリアーキと生成AIの活用〜
公開日 更新日

【アーキテクチャConference 2025】SaaS拡大期の成長痛 〜モジュラーモノリスへのリアーキと生成AIの活用〜

2025年11月20日・11月21日に、ファインディ株式会社が主催するイベント「アーキテクチャConference 2025」が、ベルサール羽田空港にて開催されました。

21日に登壇した株式会社Finatext Insurtech Domainリードエンジニアの今中 公紀さんと山崎 蓮馬さんは、SaaS型デジタル保険システム「Inspire」のリアーキテクチャについて紹介しました。本セッションでは、事業成長に伴い顕在化したアーキテクチャ上の課題と、その解決策としてのモジュラーモノリスへの移行、さらに生成AIを活用した移行作業の効率化について解説しています。

■プロフィール
今中 公紀
株式会社Finatext Insurtech Domain リードエンジニア

東京大学大学院物理学専攻卒業後、新卒で株式会社エイチ・アイ・エスのシステム部に配属。社内開発チームやオフショア拠点の立ち上げに従事。2021年に株式会社Finatextにジョインし、SaaS型デジタル保険システム「Inspire」のバックエンド開発を担当。

山崎 蓮馬
株式会社Finatext Insurtech Domain リードエンジニア

東北大学大学院情報科学研究科卒業、新卒でシンプレクス株式会社に入社。証券会社向けWebシステムのリプレースプロジェクトでフロントエンド開発を担当。その後、時価・情報配信基盤チームでバックエンド開発および運用保守に従事。2018年に株式会社Finatextにジョインし、証券口座開設システムのリプレース案件をはじめとした、金融システム開発に携わる。現在では、SaaS型デジタル保険システム「Inspire」のリードエンジニアを務める。

Finatextグループについて

山崎:Finatextは「金融をサービスとして再発明する」をミッションに掲げるフィンテックベンチャーです。2021年に上場しており、大きく3つの事業を展開しています。




山崎:1つ目は「フィンテックシフト」というセグメントで、大手金融機関向けにフロントアプリケーションの開発支援やコンサルティングを行っています。2つ目は「ビッグデータ解析」で、グループ会社のナウキャストが日経CPINOWなどの指数データを提供しています。3つ目が「金融インフラストラクチャ」で、今日のテーマの一つでもあります。証券・保険・クレジットの各領域において、SaaS型の基幹システムを提供しています。

パートナー企業が私たちのインフラSaaSを利用し、その上にフロントアプリケーションを構築してエンドユーザーに提供するという形になっています。




山崎:また、アーキテクチャカンファレンス2024では、グループ全社で共通の認証認可基盤を作った発表をさせていただきました。今回のリアーキテクチャは、この認証認可基盤を活用して進めている部分もあります。

SaaS型デジタル保険システム「Inspire」

山崎:Inspireは一言で言うと、SaaS型デジタル保険システムです。保険ビジネスに必要な一連の業務システムをAPIベースで提供する次世代基幹システムとなっています。保険においては、見積もりから申し込み、保全、保険金請求まで、一連のフローをAPIで提供できます。また、保険会社や代理店向けの業務用コンソールも併せて提供しています。

今回のリアーキテクチャの範囲は、このInspireの部分になります。





ファーストフェーズのアーキテクチャと当時の背景

山崎:保険というドメインは、実際に開発してみると非常に複雑なモデルになっています。デリバティブのように、条件次第で何でも書けてしまうような柔軟性が求められます。自然言語で書かれた条件をシステム化するのは非常に難しいことです。

また、同じ種類の保険でも会社によって内容が異なります。A社とB社の自動車保険は、一見すると違いがわかりにくいのですが、実際には細かな差異があります。何が共通概念で、何が個別要件になるかを見極めるのが非常に難しい商品です。




山崎:また当時、これを作るにあたっては、フロントエンド・バックエンド特化ではなく、ある程度フルサイクルで作れるエンジニアが必要でした。フルスタックエンジニアは希少性が高いですし、さらに保険の難しさやドメインの複雑さを理解しているエンジニアを採用・育成するのは難しかったという背景があります。

こうした状況に対して当時どういう判断をしたかというと、まずCore APIサーバーで基本的な保険の根幹に関わる部分を共通化しようと考えました。一方で、保険会社によって差異があることは先ほどお話ししましたが、その差異をCoreで吸収できるかわからなかったので、BFF(Backend For Frontend)という形で切り出しました。そこで共通化できるとなった時に、またCoreに取り込んでいくというエンハンスの方式を取ろうと決定しました。

Core APIとBFFを合わせた構成になるので、フロントエンドはCSR(Client Side Rendering)で作ろうという判断になっています。ゆくゆくはテナント共有BFFを提供して、分散したロジックを共通化しようという構想もありました。




山崎:イメージとしては、真ん中にCore APIがあって、左側に私たちが提供しているコンソールのBFFがあります。コンソールは保険会社用と代理店用の2種類に分かれています。このようにすることで、フロントはフロントで集中して開発できると考えていました。実際に保険会社Aさんが参画された際にはこのように作ったり、代理店さんが参画された際にも同様に展開したりと、目論見通りにはできました。ただ、共通化できる領域を取り込みながら増やしていくというのは、なかなか難しかったと思っています。

数年の運用を経ていくつかの課題が見えてきました。





まずBFF+CSR構成の乱立についてです。おかげさまでInspireへのニーズが非常に高く、多くの会社に使っていただいています。当然サービスが増えていくわけですが、そうなるとプロジェクトのリリースとCoreへの抽象化の優先度付けが難しくなってきます。どうしてもBFFをどんどん作ってフロントアプリを作る方が優先度が高くなりがちで、本来Coreで吸収できるようなものがどんどん分散していきました。

加えて、何が差異になっているのかを設計するのは技術力が必要な領域です。共通だと思って作ったけれど全然共通ではなかったとか、バラバラに作ったけれどこれは本質的には同じ概念だったとか、そういったことを当初から見極めるのは難しかったと思います。その結果、BFF側にドメインロジックが流出したり、マイクロサービスほど深刻ではないもののトランザクション管理が難しいといった問題が発生しました。

また、当初はモノリスで作っていました。これは人数も限られる中で、マイクロサービスやリポジトリを分散させるところにコストを割きたくないという判断でした。ただ、現在は開発体制が変わってきたところもあり、この後のリアーキテクチャにつながっていきます。

次に、マルチテナントとセキュリティの話です。金融業界ではマルチテナントがまだ受け入れられにくい状態でして、「物理的にDBが分かれていますか」といった要求をよくいただきます。それに対応するのがなかなか難しかったという経緯があります。

あとはグローバル対応です。これは今後の展望でもあるのですが、国内でサービスを提供していたから気にしなくてよかった部分が、今後は課題になってくるというところがあります。

こういった課題から、リアーキテクチャという意思決定をしました。





モジュラーモノリスへのリアーキテクチャ

今中:ここからは、リアーキテクチャを実際にどうやって進めていったかをご紹介します。タイトルにもあるとおり、モジュラーモノリスを採用したのですが、まずモジュラーモノリスとは何かというところからご説明します。

モジュラーモノリスは、厳密な仕様が定義されているものではなく、「一つのアプリとしてデプロイされるもの」という抽象的な表現でされることが多いです。一つのアプリとしてデプロイされるということは、つまりモノリスということになります。ただし、その内部はモジュールという単位で分離しています。




今中:よく説明で使われる図では、ペイメント、レビュー、ユーザーといった形で、一つひとつがモジュールになっています。一つのモノリスなのですが、マイクロサービスのようにモジュールという単位で分離していきましょうというのが、モジュラーモノリスの設計思想です。マイクロサービスとモノリスの中間的なアーキテクチャになっていると言えます。

マイクロサービスにしてしまうと、データが分散してしまったり、障害が起こった時にどこが落ちたのか分かりづらかったりという課題があります。モジュラーモノリスにしておくと、監視や運用の複雑なところを省けるので、最近注目されているのではないかと思います。

それぞれのモジュールにはPublic APIが定義されています。このPublic APIは各モジュールが公開しているAPIでして、各モジュールはPublic APIを介してお互いにやりとりすることで、一つのAPIやバッチを実装していきます。ペイメントやレビュー、ユーザーといった内部の実装詳細にはアクセスできないので、疎結合になっており、マイクロサービス的なメリットも享受できるアーキテクチャになっています。

これを実際に実装していくにあたって、まず設計として、既存のアプリをどのようにモジュラーモノリスにしていくかを議論しました。弊社のアプリはクリーンアーキテクチャをベースに作っていましたので、そこはうまくはまっていると評価していました。クリーンアーキテクチャをいかにモジュラーモノリスにしていくかを議論しました。




今中:最終的な構成としては、HandlerやApplicationレイヤーはそのままで、その配下のUsecase以下をモジュールごとに定義するようにしました。Usecase、Domain、Infrastructureは各モジュールごと、たとえば契約モジュールや通知モジュールといった形でそれぞれ定義しています。そのUsecaseのうち、外部に公開してもよいUsecaseだけをPublic APIとして公開し、外部からはこのPublic APIだけしか叩けず、内部のUsecaseは叩けないという構成にしています。

弊社はGoでサーバーサイドを書いているのですが、ディレクトリ構成としては、app/internal/module/配下に各モジュールを配置しています。Aモジュール、Bモジュールという形で、その中に各モジュールの実装が入ってきます。

ポイントとして、Goではinternalというパッケージを切ってあげると、外部からその内部にアクセスする参照ができなくなるという制約をかけられます。これにより、各モジュールが完全に独立した状態を実現できます。internalと同じ高さにusecase.goを置くことで、外部からアクセスできるものはinternalの外に見えるような形で定義しています。

アーキテクチャについてもう一つ議論したのが、DBをどう持たせるかという点です。DBの持たせ方としては、共有DBモジュールごとのDBという大きな分け方があります。




今中:共有DBは、モジュールAとモジュールBがそれぞれ同じDBにアクセスする形で、トランザクションが使えることと、DBが一つでいいので運用負荷が低いというメリットがあります。

一方、モジュールごとのDBは、モジュール単位でスケールしやすく、独立性が高いというメリットがあります。

議論の大きなポイントになったのが、弊社の事業領域であるドメインの制約です。保険ドメインは金融商品なので、お客様としてもデータの完全性や整合性を非常に気にされます。申し込んだのにデータが一時的にずれているといったことは許されない業界です。そのためトランザクションがかなり必要になってきます。また、商品性としても、急にお客様がどっと申し込んでくるとか、毎日保険サイトにアクセスして見るといったことはあまり想定されないサービスです。そのため、トランザクションも大きな課題にはならないだろうということで、共有DBで十分回せるという結論になりました。




今中:ただし、共有DBには独立性やスケーラビリティの面でデメリットがあるので、どう対処するかも議論しました。モジュールの独立性については、物理的には同じDBを見るのですが、論理的に分けていくのがよいと考えました。たとえば、契約モジュールがユーザー情報を必要とした場合、ユーザーのテーブルに直接参照しに行くことはできないようにしています。契約モジュールがユーザー情報を必要とした場合は、ユーザーモジュールにアクセスして、Public APIを通してユーザーモジュールがテーブルにアクセスしてデータを返すという形にしています。これにより、万が一マイクロサービス化したいという話があった時にも、Public APIのインターフェースが決まっていれば外出ししやすくなります。

スケーラビリティについては、先ほど山崎から紹介があった、会社によって物理的にストレージを完全に分けてほしいという要望が頻繁にありましたので、テナントごとにDBを完全に分けられるオプションを用意しました。標準はマルチテナントの共有DBですが、オプションで専用DBに切り替えられるようにしています。もしスケーラビリティでもっとスケールが必要な商品性のものが出てきた場合は、専用DBでRDSの最大のものまで使えば相当数さばけると思いますので、これで十分ではないかという設計にしています。





Public APIを介した呼び出しには、いくつかのパターンがあります。事例を調べた結果、同期の「モジュール間直接呼び出し」「Applicationレイヤー経由」「イベント駆動(同期)」と、非同期の「イベント駆動(非同期)」の4パターンがありました。




今中:同期と非同期の使い分けとしては、即時性や即時整合性が必要な場合は同期、それ以外は非同期のイベント駆動を採用する方針です。非同期イベント駆動は疎結合によるモジュールの独立性、障害の局所化、拡張性の高さがメリットです。

非同期イベント駆動の例として、「契約が成立した」というドメインイベントを発行し、通知モジュールがそれをサブスクライブしてメールを送信するケースがあります。




今中:非同期処理では不整合が発生するリスクがあります。ドメインの更新トランザクションが成功してもイベントのpushが失敗したり、その逆が起きたりする可能性があります。これを防ぐため、アウトボックスパターンをベースに実装しています。ビジネスデータの更新と同時に、同じトランザクション内でイベントテーブルにイベントを記録することで、不整合を防止しています。

同期の3パターンは、依存の性質が異なります。「モジュール間直接呼び出し」は、契約モジュールが決済を必要だと知っている場合です。「Applicationレイヤー経由」は、モジュール同士は独立しているが、ユースケースとしては同期的な実行が必要な場合です。「イベント駆動(同期)」は、契約モジュールは通知モジュールを知らないが、通知モジュール側が契約イベントをサブスクライブしている場合です。




今中:検討の結果、「モジュール間直接呼び出し」と「Applicationレイヤー経由」の2パターンを採用しました。同期イベント駆動は、イベントハンドラの実行順序制御や暗黙的な処理フローによる可読性低下、デグレのリスク、デバッグ・トレーシングの難易度の高さなど、認知負荷が高い実装方式だと判断しました。必要性が明確になった時点で再検討する方針としています。

同期処理のトランザクション管理については、外側のトランザクションを優先する方針を採用しました。上位レイヤーで既にトランザクションが開始されている場合、内側のトランザクション宣言は無視して既存トランザクションに参加します。




今中:たとえば、Applicationレイヤーでトランザクションを開始した場合、配下の全Usecaseは同一トランザクション内で実行されます。Usecase単独で呼び出した場合は、そのUsecase内でトランザクションが担保されます。

その他の課題として、B2Bでは複雑な可視性制御が求められます。部署や人ごとのアクセス制御、関連会社からのアクセスなど、SaaSとして汎用的に対応する必要があります。

私たちはミドルウェアレイヤーで透過的な可視性制御を実施しています。ユーザーから受け取ったJWTからアクセスできるスコープを特定し、AWSのIAMポリシーのようなものを生成してcontextに設定します。DBレイヤーでは自動的にフィルターがかかり、レスポンス時にもミドルウェアレイヤーで自動チェックを行います。





AWSの新機能を活用したフロントエンド刷新

今中:フロントエンド側でもAWSの新機能を活用しています。Amplify Gen 2でSSRサポートが手厚くなったため、BFF+CSR構成からSSRに移行しました。これにより、フロント、サーバ、IaCなどに分散していたコードを一元管理できるようになりました。

今中:また、RDS Data APIをSSRから利用できるようになったことで、フロントエンド側のLambdaから直接RDSを扱えるようになりました。管理コンソールでのみ利用するデータソースを、サーバサイドを意識せずに使えるため、開発効率が向上し、スピーディーな機能提供が可能になりました。





生成AIを活用したリアーキテクチャ

山崎:リアーキテクチャの検討を始めた2025年初頭は、ちょうどClaude Codeなどのエージェント型AIが実用的になってきた時期でした。リアーキテクチャには明確な変更指針があり、既存コードを参照できるためサンプルも豊富です。繰り返しの作業が多いが微妙に差分があるという特性から、生成AIで大幅に効率化できるのではと考えました。

Finatextグループでは、2025年4月時点でほぼすべてのエンジニアがコーディングに生成AIを活用している状況でした。

実際にClaude Codeでやってみた例として、保険契約の更新APIをそのままリポジトリからリポジトリに移管してくださいという指示を投げてみました。結論としては、見た目はそれっぽいのですが、結構バギーなものが出ていました。バギーというのも、見た目上は動いているように見えるのですが、実はフィールドが抜けていたり、変数名が変わっていたりしていました。動いているようには見えるのですが、プロダクションとしては品質が低いものでした。




山崎:これをどうやって解消していくかということを、ステップバイステップでいくつか紹介させていただきます。知っているものもあるかもしれませんが、やはり当たり前のことをやっていくというのが効果的だと分かっているので、あえてご紹介します。

まずはテスト駆動開発(TDD)の導入です。生成AIには明確な指示が必要です。指示が抽象的だとアウトプットもブレてしまいます。TDDは期待する結果をテストとして実装し、それを達成する実装を追加するアプローチなので、非常に明確な指示が可能です。

「テスト駆動で契約の更新APIを移行して」と指示したところ、実装はクリーンになりました。ただし、ロジックが足りていない箇所は残っており、コーディング規約の課題も継続していました。




山崎:次に、ナレッジの共有です。生成AIは毎回ゼロベースで会話が始まるため、開発メンバーなら知っている暗黙知がわかりません。そこでCLAUDE.mdにナレッジを明文化しました。アーキテクチャ、エラーハンドリング、Gitのワークフロー、設計に至る歴史的背景などを記載しています。その結果、コーディングの品質が徐々に上がり、レビューのコストも下がってきました。




山崎:さらに、タスクを分解しました。「移行」と一言で伝えても、実際には複数のステップが存在します。自分ならどう進めるかを考えてタスクを分解し、プロンプトを作成しました。具体的には、旧実装のインターフェースや仕様の調査、新アーキテクチャにおける実装設計、疑問点の確認、TDDでの実装という流れです。各フェーズの作業や期待するアウトプットを明示したことで、アウトプットの品質が向上しました。




山崎:最後に、コンテキストウィンドウの制約です。コンテキストウィンドウとは、生成AIが一度に処理できる情報量の上限です。会話履歴、ファイル内容、指示などすべてがここに含まれます。コンテキストウィンドウが90%を超えると、突然おかしな修正をしだしたり、以前の調査内容を無視した修正を行ったりする問題が発生しました。




山崎:これに対して、Claude Codeの「Subagent」という仕組みを活用しました。Subagentは別の独立したコンテキストウィンドウで動作し、親エージェントとは完全に分離されています。親エージェントはタスクの段取りを管理し、実際の調査や実装はSubagentで実行することで、コンテキストウィンドウの逼迫を防ぐことができました。

実際にやってみた感想ですが、紹介した取り組みにより、複雑すぎないケースでは精度よく実装ができるようになりました。もっと高度なこともできるとは思いますが、今回はリアーキテクチャということで、まずセキュアかつ精度高く移行するということが目標でしたので、なかなかフィットした利用ケースだったのではないかと思います。

ただ、意図せぬコードが生成されたりするのはやはり何か理由があるので、その理由をプロンプトやCLAUDE.mdを見直して修正することで、愚直にやっていくことが必要です。これは当然AIでなくても、人間と一緒にやる場合でも、ちゃんとコンテキストを与えてあげること、明確な指示をしてあげることが有用だと分かりました。

総じて、リアーキテクチャや技術的負債の返済へのハードルがぐっと下がったと実感しています。明確なコンテキストと指示を与えることで、従来見込まれた作業量を削減できることがわかりました。


今後の展望

山崎:今後は、まずアーキテクチャの移行と新機能の追加を継続して進めていきます。また、生成AIは移行だけでなく、新機能開発にも活用していきます。

さらに、私たちはB2Bのシステムを提供しているので、ユーザーやオペレーターに生成AIを使ってもらうアップデートも必要です。自分たちが使うだけでなく、使ってもらう方法も考えていく必要があります。ご清聴ありがとうございました。





アーカイブ動画・発表資料

イベント本編は、アーカイブ動画を公開しています。また、当日の発表資料も掲載しています。あわせてご覧ください。

▼動画・資料はこちら
アーキテクチャConference 2025

※動画の視聴にはFindyへのログインが必要です。

資料ダウンロード

必要事項を記入のうえ、「この内容で送信する」ボタンを押してください。

  • ツールに関するご提案や最新情報の提供のため、資料ダウンロード後にFindy Toolsを契約している資料に該当する協賛会社(以下「協賛会社」といいます)から、記載いただいた情報をもとにご案内を差し上げる場合があります。
  • 上記ご案内のため、上記記載内容ならびにFindy Toolsにご登録いただいたユーザー情報を当社から協賛会社に対して提供いたします。