業務フローをなめらかにするために絡み合う複数プロダクトに、マイクロサービスならどう向き合うかの話です。LayerXのバクラクシリーズの話です。
完璧な設計&アプローチとかではなく、現実にあったケーススタディとして、優しく見ていただけると幸いです。
Confidential © 2022 LayerX Inc.1絡み合うSaaSプロダクトのマイクロサービスアーキテクチャ2022/03/15@mosa_siruSaaS.tech #1
View Slide
Confidential © 2022 LayerX Inc.2これはなに● 業務フローをなめらかにするために絡み合う複数プロダクトに、マイクロサービスならどう向き合うかの話です。● 完璧な設計&アプローチとかではなく、現実にあったケーススタディとして、優しく⾒ていただけると幸いです。● めちゃくちゃボリューミーです、すいません…
Confidential © 2022 LayerX Inc.3⾃⼰紹介榎本 悠介 @mosa_siru・LayerX取締役 SaaS事業部⻑・プロダクト/エンジニアチームをリード・創業時CTO。新規事業、新規プロダクトをひたすらつくるマン・前線でプロダクト仕様考えまくってコードかきまくってます
Confidential © 2022 LayerX Inc.4⽬次1. プロダクト紹介2. 基本の構成図と考え⽅3. マイクロサービスのケーススタディ(循環参照を避ける話)4. マイクロサービスのケーススタディ(レプリケーション)5. アーキテクチャ再考6. まとめ
Confidential © 2022 LayerX Inc.51. プロダクト紹介
Confidential © 2022 LayerX Inc.62021/01から、3つのプロダクトを順次展開(どれも単体で便利︕)特に今回、この2つに注⽬
Confidential © 2022 LayerX Inc.7AI OCRによる⾃動読取仕訳の⾃動⼊⼒補完・振込データ⾃動⽣成会計システムとシームレスに連携AI OCRによる稟議の⾃動下書・申請補助購買申請との紐付けで予算管理Slack連携でSlackで承認が完結めちゃくちゃ便利な請求書処理プロダクトめちゃくちゃ便利な汎⽤ワークフロー(申請&承認)プロダクト
Confidential © 2022 LayerX Inc.8企業A 企業B売り⼿・受注者 買い⼿・発注者請求書を発⾏請求書を受け取り会計・⽀払処理バクラク請求書とは受け取った請求書の処理を効率化するサービスです
Confidential © 2022 LayerX Inc.9Before バクラク請求書✓ 請求書を⾒ながらシステムに⼿⼊⼒が多い。⼊⼒ミスも多い。✓ 前⽉の仕訳を確認しながら、仕訳を切っていて⼤変。⼿⼊⼒・ミス回収漏れ✓ 現場が請求書を上げ忘れる。取引先が請求書を送ってこない。✓ 請求書の抜け漏れチェックが⼤変。稟議・統制✓ 現場が上げる⽀払依頼が遅い。申請が間違っている。✓ 請求書だけ⾒ても、⽀払っていいかわからない。請求書と稟議が紐付いていない。(紙で申請と請求書がまわってくる…)今回、ここにフォーカス当てます
Confidential © 2022 LayerX Inc.10After バクラク請求書✓ AI-OCRで請求書情報を⾃動⼊⼒✓ 仕訳の⾃動⼊⼒補完・振込データ⾃動作成✓ 回収状況レポートで回収漏れを確認✓ 回収催促機能で、請求書をURLで回収可能✓ 稟議をOCRで⾃動⼊⼒。現場でミスや負担削減✓ 請求書と稟議の紐付けでかんたん確認⼿⼊⼒・ミス回収漏れ稟議・統制今回、ここにフォーカス当てます
Confidential © 2022 LayerX Inc.11回収 稟議・承認 仕訳・⽀払・消込 保管各プロセスの⾮効率を解消し、⼀気通貫の業務に✓抜け漏れ確認✓請求書催促✓AI OCRで申請補助✓購買申請で予算統制✓slackでラクラク承認✓AI OCRで⾃動読取✓仕訳データの⾃動作成✓⽀払データ⾃動作成✓電⼦帳簿保存法対応データを⾃動同期バクラクシリーズで⼀気通貫の業務プロセスのデジタル化が可能
Confidential © 2022 LayerX Inc.12ここだけおさえておいて欲しいポイント︕● 経理に請求書だけ来ても、経理は会社の全てを把握しているわけではないので、払っていいかわからなかったりするよ○ 上⻑がその⽀払を承認していたか、元の”⽀払申請”(請求書がきたので払ってください申請)を⾒る必要があるよ● でも承認プロセスと請求書処理業務は普通「分断」されているから、その紐付けが⼤変だよ○ 例︓承認された⽀払申請の内容を、プリントアウトして経理に渡している● バクラク申請とバクラク請求書は、そこが連携されているのがポイントだよ○ ⽀払申請がされると、バクラク請求書側に請求データが作られるよ(単体で便利なプロダクトたちだけど、2つ合わさるとさらに便利なんだよ)
Confidential © 2022 LayerX Inc.132. 基本の構成図と考え⽅
Confidential © 2022 LayerX Inc.14構成図(基本形)各プロダクトのFrontendが裏側の専⽤APIを叩く形式。DBは共有しない。例︓ユーザー情報については、共通管理基盤(認証基盤)から取りにいく
Confidential © 2022 LayerX Inc.15⽅針● SPA + Backend APIの構成 (Nuxt.js + Golang)● 各プロダクトごとのマイクロサービス○ 各プロダクトにPublic APIとPrivate APIがあり、裏側ではPrivate APIを利⽤● DBはマイクロサービスをまたいで共有しない (ReadもWriteも⼀⼈)● プロダクト間の循環参照は避ける(重要)○ デプロイ順が意味不明になるため
Confidential © 2022 LayerX Inc.16Why Microservices?● 単体で成⽴するSaaSプロダクトは、ドメイン境界として適切に⾒えた● 実際、開発チームは分かれていた&チームをスケールさせる覚悟があった● 今後どんどんプロダクトを出す可能性があった● 複数プロダクトを展開するSaaSの先輩たちが、モノリスからマイクロサービスに切り出すのに苦⼼しているのを知っていた
Confidential © 2022 LayerX Inc.173. マイクロサービスのケーススタディ(循環参照を避ける話)
Confidential © 2022 LayerX Inc.18ここからが本題
Confidential © 2022 LayerX Inc.19回収 稟議・承認 仕訳・⽀払・消込 保管各プロセスの⾮効率を解消し、⼀気通貫の業務に✓抜け漏れ確認✓請求書催促✓AI OCRで申請補助✓購買申請で予算統制✓slackでラクラク承認✓AI OCRで⾃動読取✓仕訳データの⾃動作成✓⽀払データ⾃動作成✓電⼦帳簿保存法対応(※ 2022年1⽉~対応予定)データを⾃動同期バクラクシリーズで⼀気通貫の業務プロセスのデジタル化が可能
Confidential © 2022 LayerX Inc.20回収 稟議・承認 仕訳・⽀払・消込 保管各プロセスの⾮効率を解消し、⼀気通貫の業務に✓抜け漏れ確認✓請求書催促✓AI OCRで申請補助✓購買申請で予算統制✓slackでラクラク承認✓AI OCRで⾃動読取✓仕訳データの⾃動作成✓⽀払データ⾃動作成✓電⼦帳簿保存法対応(※ 2022年1⽉~対応予定)データを⾃動同期バクラクシリーズで⼀気通貫の業務プロセスのデジタル化が可能データを⾃動同期︕︕
Confidential © 2022 LayerX Inc.21要件(1)バクラク申請で⽀払申請(請求書がきたので払ってください申請)がされると、申請内容を元にしてバクラク請求書で請求書レコードが作られる (Write)申請すると、申請内容を元に請求書レコード作成請求書No. 111⽀払⾦額: 660,000⽀払期⽇: 2020-10-31取引先︓株式会社⽇本(添付ファイル付)申請作成画⾯
Confidential © 2022 LayerX Inc.22要件(1)バクラク申請で⽀払申請(請求書がきたので払ってください申請)がされると、申請内容を元にしてバクラク請求書で請求書レコードが作られる (Write)
Confidential © 2022 LayerX Inc.23要件(2)バクラク請求書で請求書を⾒るときは、元になった申請情報も⾒える(Read)(経理がパッと確認するため)
Confidential © 2022 LayerX Inc.24要件(2)バクラク請求書で請求書を⾒るときは、元になった申請情報も⾒える(Read)(経理がパッと確認するため)
Confidential © 2022 LayerX Inc.25ごく普通の要件感あるが、すでにプロダクト間で循環する・・・・
Confidential © 2022 LayerX Inc.26どうするか
Confidential © 2022 LayerX Inc.27考えた⽅法A. BFF / Gateway API を利⽤して、循環を避けるB. SPAが他プロダクトのPublic APIを叩くのを許すC. 必要なデータを同期してしまう
Confidential © 2022 LayerX Inc.28(A) BFF / Gateway API を利⽤して、循環を避ける
Confidential © 2022 LayerX Inc.29(A) BFF / Gateway API を利⽤して、循環を避ける● メリット○ シンプルである● デメリット○ 今は存在しないGateway APIが登場する■ ※そもそもあるべきという説も濃厚であるが、各種事情で今からつくるのが簡単ではない○ Gatewayがロジックを持たないなら、結局依存関係が発⽣(次ページ)
Confidential © 2022 LayerX Inc.30Gatewayが更新制御ロジックを持たないなら、結局依存関係が発⽣Gateway API には 2つのマイクロサービスをまたいだ更新制御ロジックを持たせたくない。(ロールバック処理等をGatewayに持たせたくないため。)その場合、バクラク申請 => バクラク請求書の依存が発⽣する。この理屈だと、更新処理の起点がバラバラだと、結局循環参照が起きる可能性がある。
Confidential © 2022 LayerX Inc.31(B) Frontend が各プロダクトのPublic APIを叩くのを許す
Confidential © 2022 LayerX Inc.32(B) Frontend が各プロダクトのPublic APIを叩くのを許す● よくある、BFFを導⼊する前のパターン。⽐較的シンプルではある● 場合によっては結局依存が発⽣する(次ページ)● 認証・権限まわりがカオスになりがち○ 例︓バクラク申請の権限が⾜りず、バクラク請求書側で元になった申請を⾒ることができない(あるべき姿かもしれないが、混乱の元になりうる)
Confidential © 2022 LayerX Inc.33Frontendが更新制御ロジックを持たないなら、結局依存が発⽣両プロダクトの更新処理をFrontendから投げるのは、トランザクション管理を考えるとおすすめしない。backend経由にする場合、結局バクラク申請 => バクラク請求書の依存は発⽣する。(GatewayAPIの時と同様)循環参照を避けるには、更新の起点を統⼀する必要がある。
Confidential © 2022 LayerX Inc.34(C) 必要なデータを同期してしまう
Confidential © 2022 LayerX Inc.35(C) 必要なデータを同期してしまう● メリット○ バクラク請求書ユーザーは、申請側の権限に依らず申請情報を⾒ることができる● デメリット○ データ同期の複雑性○ データ不整合の危険○ DBスキーマ変更時に⼤変これやるくらいなら、DB直参照を許した⽅が良いのではという気分にならなくもない。(Writeは1⼈の責任、Readは最悪複数⼈を許す)
Confidential © 2022 LayerX Inc.36さらに要件は続く
Confidential © 2022 LayerX Inc.37よく考えたら出てきた⾟い要件(3)バクラク請求書側の請求書⼀覧の検索にて、元の申請のステータス(承認済等)でフィルタしたい(例︓承認済の請求書だけ処理したい)
Confidential © 2022 LayerX Inc.38これはやばい● 請求書⼀覧のフィルタは、請求書の⽇付や⾦額等、あらゆる条件の掛け合わせで⾏われる。申請ステータスはその⼀部。● これを最適なページング込みで実現するには、同じDBに持つしかない
Confidential © 2022 LayerX Inc.39よく考えたら出てきた⾟い要件(4)バクラク請求書側で、元になった申請のデータと合わせて請求書リストをデータ出⼒したい請求書No 取引先名 ⾦額 元の申請No 元の申請タイトル#123 mosa産業 10,000 #12 ⽀払申請(mosa産業)#121 ymatsu⽔産 20,000 #9 ⽀払申請(ymatsu⽔産)#120 … … .,.. …
Confidential © 2022 LayerX Inc.40これもやばいA. Gateway APIパターン︓ Gateway APIでCSV組み⽴てるの・・・︖B. Frontend パターン︓Frontでデータの組み⽴てをすることは現実的ではない。結局、バクラク請求書 => バクラク申請の依存が発⽣する(下図)これにより循環参照となる。C. データ同期して、同じDBに持つのがベストではという結論
Confidential © 2022 LayerX Inc.41データ同期の実現⽅法 3つ
Confidential © 2022 LayerX Inc.42実際は、API経由ではなく⾮同期でデータ同期するAPI経由だと● 同期処理によりレスポンスが遅くなる● 他サービスの障害に影響して、本来の処理(申請作成)⾃体も失敗してしまう。● トランザクション境界が分かれているため、失敗時にデータ不整合がありうる=> Queue経由でリトライ機構を⼊れ、結果整合とする。Private APIは微妙…
Confidential © 2022 LayerX Inc.43パターン1: Push型必要な情報をまるっと渡し、⾮同期で更新。必要なコンポーネントも少なく、シンプル。queueのpayloadは太りがち。バクラク申請 => バクラク請求書 の依存が発⽣する。
Confidential © 2022 LayerX Inc.44パターン2: Pull型申請IDのみを渡し、実体は取りにこさせる形式。queueのpayloadがシンプルで扱いやすい。軽微な循環参照だが、今後 バクラク申請 <= バクラク請求書 の依存(Push型と逆の依存)の⽅向が強くなりそうな場合、有⽤。
Confidential © 2022 LayerX Inc.45パターン3: 仲介型複雑だが、専⽤のコンポーネントを仲介することで、直接的な依存を発⽣させない。
Confidential © 2022 LayerX Inc.46パターンのまとめA => B の依存 A <= B の依存 コンポーネント数1. Push型 強 - 少2. Pull型 弱 強 中3. 仲介型 弱 - 多どの依存関係を作りたいかによって、アプローチが異なる。=> 僕らは今後どの依存関係がメインになりそうか︖顧客の声をもとに、将来ありそうな要件をベースに考えた。
Confidential © 2022 LayerX Inc.47開発を進めるうちに出てきた要件(5)バクラク申請のサービスで、現在バクラク請求書で保持しているマスタデータを⼊⼒したい(例︓申請にて、どの部⾨で使われた費⽤かを⼊⼒・表⽰させたい)
Confidential © 2022 LayerX Inc.48おそらくバクラク申請 => バクラク請求書の依存が発⽣しやすいドメインと理解補⾜︓今回の要件ならGateway APIがあれば依存は発⽣しないように⾒えるが、「申請⼀覧を出⼒したい」と⾔われるとやはりアウト
Confidential © 2022 LayerX Inc.49【結論】依存関係の整理の結果、シンプルな「 1. Push型」にしたA => B の依存 A <= B の依存 コンポーネント数1. Push型 強 - 少2. Pull型 弱 強 中3. 仲介型 弱 - 多
Confidential © 2022 LayerX Inc.50補⾜︓データの置き場所● 依存を発⽣させないために、各プロダクトから参照されやすいマスタデータを、そもそも別サービスに切り出す⼿もある● 「どこから更新されるか」「どこから参照したくなるか」あらゆるユースケースを想像して、「誰の持ち物か」を考え続ける必要がある○ 事前に決めるには、めちゃくちゃドメイン知識がいる。設計レベルでドメインエキスパートと相談していた。● 実際、とあるマスタデータは後から最適な場所に移動させた。
Confidential © 2022 LayerX Inc.51補⾜︓データ同期の整合性担保不整合をおこさないために・・・● 当然リトライする● データ同期ジョブについて、当然監視をする● 最終的にデータに不整合がおきていないか、定期的に確認し、アラートを⾶ばしている
Confidential © 2022 LayerX Inc.524. マイクロサービスのケーススタディ(レプリケーション)
Confidential © 2022 LayerX Inc.53他にもデータ同期が必要なデータが現れだした● バクラク申請にて、他のRDBとJOINしないと、パフォーマンス的に厳しいクエリが出てきた(詳細略)○ (この時点でドメイン境界とは…など⾊々⾔いたいことはある⽅はいそうですが、まだ抑えてください)● これは「申請をもとに請求書レコードをつくり、さらに申請データも同期する」等ではなく単純なレプリケーションで良いため、前述と異なる同期パターンが考えられた
Confidential © 2022 LayerX Inc.54同期パターン1 Push型 / パターン2 Pull型 / パターン3 仲介型上述と同様。(下図はパターン1)
Confidential © 2022 LayerX Inc.55パターン4︓定期Pull型定期実⾏して、差分のあるレコードを取得し、更新する。
Confidential © 2022 LayerX Inc.56パターン4︓定期Pull型クライアントサイドは「最終更新時刻」を持っておき、「それ以降に更新されたレコード⼀覧をくれ︕」と⾔うイメージ。● メリット○ パターン1-3と異なり、とりこぼしがなく、データ整合性を保ちやすい● デメリット○ リアルタイム性が低い○ ⼀括でデータ更新されていると同期量が多い○ 物理削除されたことをハンドリングするにはひと⼿間いる
Confidential © 2022 LayerX Inc.57パターン5︓DBをまたいでレプリケーションする● 例︓AWS DMS(hostをまたいで特定テーブルのレプリが可能)○ 1回限りではなく、継続的なレプリケーションが可能
Confidential © 2022 LayerX Inc.58パターン5︓DBをまたいでレプリケーションする● AWS DMSを素振りしたところ、問題なくhostをまたいでレプリケーションされた● ALTERもsyncされることを確認した○ ※カラムのデフォルト値, nullability, charset の変更は同期されないので注意● ⼀⽅、localでの開発環境をどうするかが課題● (そして同様のユースケースで使っているのを⾒たことがなく、不安)
Confidential © 2022 LayerX Inc.59【結論】● 今回のケースではデメリットを許せたため、「パターン4︓定期Pull型」とした● やはりユースケースによって最適な⼿段は変わる
Confidential © 2022 LayerX Inc.605. アーキテクチャ再考
Confidential © 2022 LayerX Inc.61そして現在、まだまだ増え続ける連携ニーズ「バクラク請求書のファイルをバクラク電⼦帳簿保存に連携したい」「バクラク申請のファイルをバクラク電⼦帳簿保存に連携したい」「バクラク電⼦帳簿保存にあがったファイルを、バクラク申請に使えないか」
Confidential © 2022 LayerX Inc.62俺たちは⼀体何と戦っているんだ︖
Confidential © 2022 LayerX Inc.63そもそもドメイン境界間違えてない︖モノリスじゃだめなの︖
Confidential © 2022 LayerX Inc.64実際やってみた
Confidential © 2022 LayerX Inc.65モジュラモノリス化のトライ● mosaがマイクロサービスに発狂し、1-2週間集中して取り組んだ。● せっかく分かれているDBは⼤統⼀せずに、モジュラモノリスっぽくやるのを検討した
Confidential © 2022 LayerX Inc.66当初考えていたメリット1. 循環参照問題からの解放2. データ同期と整合性チェック、Txまたぎの必要がなくなる3. コード共有ができる、無駄なPrivate API開発がなくなる
Confidential © 2022 LayerX Inc.67結論︓撤退した
Confidential © 2022 LayerX Inc.68撤退の理由● 循環参照問題からの解放はされなかった。○ モジュラモノリス内で循環依存を避けるために、結局同じことを考える必要があった○ 例︓申請情報をembedする請求書struct があると、package依存が発⽣○ コンポーネント間の複雑さを、そのままコード間の複雑さにおしこめた印象だった。● DBが分かれている時点で、Txまたぎと不整合リスクは残った● 組織のスケールという流れに逆⾏している● 思ったより共有されるロジックはなかった● コードの⾒通しが悪くなった。書いていて楽しくなくなった(フィーリング)
Confidential © 2022 LayerX Inc.69本当に試すべきは真のモノリス & DBの統⼀化だったか● 仮にモジュラモノリスがうまく⾏くなら、今のマイクロサービスでもうまくいくはず。● やるならDBも統⼀、循環依存上等な完全なモノリス化だが、今のメリットを完全に捨てることになる。モジュラモノリスでない以上、戻ることも茨の道。ゼロベースならさておき、今からやるには…。
Confidential © 2022 LayerX Inc.70あらためて、今の構成のメリットは何か︖
Confidential © 2022 LayerX Inc.71連携の設計は難しいが、マイクロサービスのメリットを享受しているのも事実● 各位が⾃律して動き、ファクトとして爆速で開発できており、運⽤もできている○ いまも現在進⾏形で新しいプロダクトを開発しようとしている● 他にも、OCR基盤/認証基盤/タイムスタンプ機構などプロダクトによらないマイクロサービスもあり、効率化の恩恵を感じている● そもそも単体で成⽴しているプロダクトであり、「⼀部でもデータ連携(の可能性)があるから分けるべきでない」というのもどうなのか○ 今後新しいプロダクト群も全部モノリスにするのか︖● 新しいプロダクトでは新しい開発⼿法を試すことができ、確実にチームに知⾒がたまっている
Confidential © 2022 LayerX Inc.726. まとめ
Confidential © 2022 LayerX Inc.73まとめ● 複数プロダクトで業務フローをなめらかにするSaaSは、ドメイン境界の設定が単純ではない○ 適切な切り⽅を知るには、深いドメイン知識が必要で、後からわかることも多い● 連携部分とデータの置き場所は、設計⼒が極めて問われ、ユースケースに依存する● 割り切って最初からモノリスにすることは⼗分ありうる。⼀⽅、先輩⽅が後から切り出すのに苦労しているのも事実● モジュラモノリスも結局依存関係と戦うことになると実感
Confidential © 2022 LayerX Inc.74We are Hiring︕LayerXでは、顧客と向き合い、最⾼の体験と最適な設計を両⽴できるエンジニアを求めています︕https://jobs.layerx.co.jp/