A B O U TRust製の業務WebアプリケーションをRustでリプレイス
View Slide
Yoshiki Matsuda2● バックエンドエンジニア @CADDi● キャディでは受発注業務アプリケーションをRustで開発● 前職ではGoでバックエンド開発● 開発環境○ Neovim○ wezterm○ Arch Linux
3今日お話しするプロダクトサプライチェーンの可視化発注先パートナーの選定※ リプレイス前の画面です
なぜリプレイスするのか?4● 「多品種少量・受注生産」のみならず「中量産・見込み生産」へとビジネスが拡張BOM(部品表)製品製品製品製品見積受注発注発注案件製品製品製品発注発注案件製品製品製品発注発注
既存コードベースの拡張ではダメだった?5● 既存コードベースの拡張でも、対応できなくはなかった○ Entity間の関係性を全面的に見直す必要がある● 工数を考えると、リプレイスしたほうが得と判断○ 互換性確保のために工数を割く必要がない○ 現在のビジネスモデルを前提に最適な設計ができる○ ついでに技術的負債も返済できる● 社外ユーザー様が関わる部分のみ互換性を確保
6リプレイスの全体像
リプレイスの方針7● YAGNI(You Ain’t Gonna Need It)を重視○ 早すぎる抽象化・最適化を避ける● 学習コストの削減○ 一貫性のある規約によってコードベースを構築○ ドメインロジックに集中できる状態● テスタビリティの向上○ 外部I/Oはすべてモック化可能な設計に○ 結合テストも自動化する
全体アーキテクチャ8マイクロサービスはコンテキスト単位で分割- BOM(部品表)- 見積- 受注- 発注- サプライチェーン設計- 取引先情報- ユーザ情報- etc…DBは将来的に分割できるようマイクロサービスごとにスキーマを分けて運用マイクロサービス群の外側の他サービスとはCloud Pub/Subで連携(gRPCを同期的に叩いているケースもあり)マイクロサービス間はgRPC APIで同期的に通信コンポーネント横断での開発を促進するため、リプレイス対象のマイクロサービス群の開発言語はRustに統一
なぜRust?9● 型の表現力の高さと堅牢性○ Option / Result○ パターンマッチ○ trait● キャディでは最も利用経験者の多い言語○ 現状もRustなので当たり前ではあるのですが……○ チーム間の人員流動性を確保できる● 社内に蓄積された知見を利用できる
Rustへの懸念10● 新規メンバーの学習コストが大きいのでは?○ 他の言語でも、言語未経験の新規メンバーに一定の学習コストが発生するのは同じ○ これまでに入社したRust未経験メンバーも無事にキャッチアップできている● 外部SDKが提供されない等で困るケースが発生するのでは?○ これまでの経験上、大きな問題は生じていない○ 仮にそのようなケースが発生した場合、外部SDKに依存するマイクロサービスのみ別の言語で開発する選択肢もある
11リプレイスのポイント
Monorepo12● UI / BFF / Microservices を単一のGitHub Repositoryに共存させるモノレポ構成を導入○ Repository間の移動によるコンテキストスイッチを軽減○ path-filteringにより、変更されたファイルのパスに応じてCIを制御● マイクロサービスの雛形を自動生成するスクリプトを用意○ 自動生成→CIに組み込むだけで開発環境にサーバーが立ち上がるディレクトリ構成./- .circleci- proto- rust- common- service-a- service-b- service-c- typescript- apps- ui- bff
Clean Architecture13● ドメインロジックはDomain層に集約● Infra層に対してはtraitを経由して依存するDomainUsecaseInfraRepositoryTraitRepositoryImplディレクトリ構成service-a- app- domain- usecase- infra- db_dto- repository_impl- grpc- grpc_handler- grpc_convert- common- external_libs- error
ドメイン駆動設計14● 境界付けられたコンテキストの内側に集約を定義○ 集約は1つまたは複数のエンティティから構成される● データの更新は必ず集約単位で行う○ それぞれの集約は、集約内部のデータの不変条件を担保BOM(部品表)コンテキスト集約 集約見積コンテキスト集約 集約受注コンテキスト集約 集約発注コンテキスト集約 集約
crateを細分化15● crateは他の言語でいうパッケージやモジュールに相当● 1つのマイクロサービスの内部を多数の小さなcrateに分割○ ビルド時間短縮による開発者体験の向上○ リプレイス前は一部のcrateが肥大化し、ビルド時間のボトルネックとなっていたDomainUsecase A Usecase BgRPC Handler A Repository A Repository BApp (Entrypoint)gRPC Handler B
テスタビリティ向上16● mockallクレートでRepositoryのtraitをモック化して単体テスト○ リプレイス前は、データベースI/Oに独自の社内ライブラリを利用していた関係で、Usecase層のテストが書きづらかった● 結合テストのコードもRustで書いている○ gRPCリクエストを飛ばすテストコード専用のcrateを定義○ cargo testで走らせる
苦労していること17● ドメインモデリング○ 事業規模拡大により、案件担当者、検品拠点スタッフ、会計担当者など、ユーザーごとのニーズの多様性が増大○ 見込み発注、在庫引当、装置一式組立……● マイクロサービス○ サービス間通信、分散トレーシング、サービスメッシュ……● テスト・QA○ Rustの型の厳しさの代償として、テストコードを書くコストが重い○ UIまで含めたe2eテストも自動化したいが、まだ手つかず
18おわりに
We are hiring!19● 以下のような方、一緒に開発しませんか?○ 複雑な業務ドメインのモデリングが好きな方○ 高速で変化するビジネスモデルに対応できる柔軟なソフトウェアアーキテクチャを探求したい方○ モダンな技術をフル活用して事業価値を実現したい方● ぜひカジュアルにお話ししましょう○ ご応募はこちらから: https://corp.caddi.jp/recruit/eng