/note/tech

SaaS管理ユニットはなぜモジュラーモノリスから始め、マイクロサービスへ移行しているのか

要約:

■ 1. SaaS管理ユニットの概要

  • SmartHRは人事労務領域に加え、情報システム部門向けサービスにも取り組む
  • 2023年10月、メタップス社から「メタップスクラウド」の事業譲渡をきっかけに情シス領域への取り組みを開始
  • SmartHRに集まる従業員データを活用し、外部SaaSへのSSO(シングルサインオン)やアカウント管理の効率化を実現
  • SaaS管理ユニットが開発する主な機能:
    • IdP機能:SmartHRを起点に外部SaaSへのログインを提供
    • ID管理機能:従業員データをもとに外部SaaSのアカウントを可視化・作成・削除

■ 2. 立ち上げ時にモジュラーモノリスを選んだ理由

  • SmartHRの基本機能のモノリス内にモジュールを作る形で開発を開始
  • 選択の利点:
    • 新規インフラを一から構築する必要がない
    • 既存の認証・権限・デプロイ基盤をそのまま利用可能
    • 同一データベース上で開発でき、従業員データをActive Record経由で扱いやすい
    • SSO対象従業員の制限、SaaSアカウントの所有状況可視化といった機能を容易に実現
  • 結果としてIdP機能を約半年でリリース

■ 3. モジュラーモノリスで顕在化した課題

  • 可用性の課題:
    • 基本機能側のメンテナンスやインシデント発生時にIdP機能・ID管理機能も停止する
    • 年末調整などの労務機能メンテナンスや基本機能内の別プロダクト障害が、外部SaaSへのログイン体験に影響する構造
  • パフォーマンスの課題:
    • 基本機能はBitemporal Data Modelを採用しており、関連テーブルやカラムが多い複雑な構造
    • 情シス領域から不要なテーブル・カラムが存在しても基本機能側のデータ構造に依存するため整理が困難
    • ユースケースに合わせたインデックス追加の判断がしづらく、従業員数増加に伴い問題が拡大
  • 開発体験の課題:
    • CIに30分以上かかる場合がある
    • デプロイは1日1回に限定され、ステージング反映までに時間を要する
    • ローカル開発でもrails sの起動に時間がかかり、フロントエンドからAPIを呼び出すだけでも待ち時間が発生
    • AIを活用しながら一人ひとりがフィーチャーを担当する開発体制では、小さく修正してすぐ確認できる環境が重要であり、待ち時間が開発の勢いを妨げる

■ 4. マイクロサービスへの段階的移行プロセス

  • 移行方針:一気に切り離さず、依存関係をほどきながら段階的に独立性を高める
  • ステップ1:基本機能への依存の整理
    • Packwerkを活用し、基本機能側のデータへの直接アクセスをPublic API経由のメソッド呼び出しに置き換え
    • 切り離す前に境界を可視化し、どこが基本機能に依存しているかをコード上で確認可能な状態を実現
  • ステップ2:依存のHTTP通信への置き換え
    • メソッド呼び出しをHTTP通信に置き換えることで、サービス間通信の形で境界を扱えるよう移行
    • リポジトリ分離前に通信の失敗やレスポンスの扱いも含めてサービス間通信のインターフェイスを育てる
  • ステップ3:リポジトリの分離(現在進行中)
    • IdP機能・ID管理機能のコードベースを基本機能から切り出し、インフラも新規に構築して分離
    • ストラングラーパターンを用いて既存のAPIを段階的に移行し、VPC内HTTP通信で必要なデータにアクセス
    • 新旧APIが共存する期間は腐敗防止層を設けて差分を吸収しながら移行を継続
  • 今後の予定:データベースの移行
    • バッチやWorkerによるデータ同期を通じて、SaaS管理ユニット側のサービスが必要なデータを自前で保持できる構成へ移行
    • 基本機能側のデータ構造への直接依存から脱却し、可用性・パフォーマンス面での独立性を向上

■ 5. 移行によって得られた効果

  • デプロイ速度の改善:
    • 1日1回の制限から解放され、対象サービスの都合に合わせて随時デプロイ可能に
  • CI時間の大幅短縮:
    • 移行前:モノリス全体のCIで30分以上
    • 移行後:対象サービスに閉じたCIで約1分
  • ローカル開発環境の高速化:
    • rails sの起動が速くなり、APIの挙動確認までの待ち時間を短縮
  • 新しい開発基盤の導入が容易に:
    • RubyDex、Sorbet、Caddyなどをサービス単位で導入・運用可能
    • モノリス全体への影響を考慮せず、小さな単位で新技術を取り入れられる環境を実現
  • パフォーマンスの改善(移行途中段階の計測値):
    • 1,000件:207ms → 57.6ms(3.59倍高速化)
    • 10,000件:2.30s → 614ms(3.75倍高速化)
    • 50,000件:13.7s → 3.07s(4.46倍高速化)
    • 従業員データテーブルの行サイズを約71%削減

■ 6. 振り返りと教訓

  • モジュラーモノリスで始めることとマイクロサービスへ移行することは対立する選択肢ではない
  • 最初から別サービスとして構築していた場合、インフラやデータ連携の準備に時間を要しIdP機能の早期リリースは困難であった
  • モノリス内でもモジュールとして境界を明確にしていたことで、後から責務の判断や移行の起点の特定が容易になった
  • PackwerkによるPackage間・サービス間の依存可視化が移行を進める際に有効に機能した
  • 得られた教訓:最初から完成形のアーキテクチャを選ぶことよりも、その時点で価値を届けやすい形を選びつつ、後で変えられる余地を残しておくことが重要

関連: