Slide 1

Slide 1 text

A B O U T Rust製の業務Webアプリケーションを Rustでリプレイス

Slide 2

Slide 2 text

Yoshiki Matsuda 2 ● バックエンドエンジニア @CADDi ● キャディでは受発注業務アプリケーションをRust で開発 ● 前職ではGoでバックエンド開発 ● 開発環境 ○ Neovim ○ wezterm ○ Arch Linux

Slide 3

Slide 3 text

3 今日お話しするプロダクト サプライチェーンの 可視化 発注先パートナーの選定 ※ リプレイス前の画面です

Slide 4

Slide 4 text

なぜリプレイスするのか? 4 ● 「多品種少量・受注生産」のみならず「中量産・見込 み生産」へとビジネスが拡張 BOM (部品表) 製品 製品 製品 製品 見積 受注 発注 発注 案件 製品 製品 製品 発注 発注 案件 製品 製品 製品 発注 発注

Slide 5

Slide 5 text

既存コードベースの拡張ではダメだった? 5 ● 既存コードベースの拡張でも、対応できなくはなかった ○ Entity間の関係性を全面的に見直す必要がある ● 工数を考えると、リプレイスしたほうが得と判断 ○ 互換性確保のために工数を割く必要がない ○ 現在のビジネスモデルを前提に最適な設計ができる ○ ついでに技術的負債も返済できる ● 社外ユーザー様が関わる部分のみ互換性を確保

Slide 6

Slide 6 text

6 リプレイスの全体像

Slide 7

Slide 7 text

リプレイスの方針 7 ● YAGNI(You Ain’t Gonna Need It)を重視 ○ 早すぎる抽象化・最適化を避ける ● 学習コストの削減 ○ 一貫性のある規約によってコードベースを構築 ○ ドメインロジックに集中できる状態 ● テスタビリティの向上 ○ 外部I/Oはすべてモック化可能な設計に ○ 結合テストも自動化する

Slide 8

Slide 8 text

全体アーキテクチャ 8 マイクロサービスはコンテキスト単位で分割 - BOM(部品表) - 見積 - 受注 - 発注 - サプライチェーン設計 - 取引先情報 - ユーザ情報 - etc… DBは将来的に分割できるよう マイクロサービスごとに スキーマを分けて運用 マイクロサービス群の外側の他サービスとは Cloud Pub/Subで連携 (gRPCを同期的に叩いているケースもあり) マイクロサービス間はgRPC APIで 同期的に通信 コンポーネント横断での開発を促進す るため、リプレイス対象のマイクロ サービス群の開発言語はRustに統一

Slide 9

Slide 9 text

なぜRust? 9 ● 型の表現力の高さと堅牢性 ○ Option / Result ○ パターンマッチ ○ trait ● キャディでは最も利用経験者の多い言語 ○ 現状もRustなので当たり前ではあるのですが…… ○ チーム間の人員流動性を確保できる ● 社内に蓄積された知見を利用できる

Slide 10

Slide 10 text

Rustへの懸念 10 ● 新規メンバーの学習コストが大きいのでは? ○ 他の言語でも、言語未経験の新規メンバーに一定の学習コストが発生 するのは同じ ○ これまでに入社したRust未経験メンバーも無事にキャッチアップでき ている ● 外部SDKが提供されない等で困るケースが発生する のでは? ○ これまでの経験上、大きな問題は生じていない ○ 仮にそのようなケースが発生した場合、外部SDKに依存するマイクロ サービスのみ別の言語で開発する選択肢もある

Slide 11

Slide 11 text

11 リプレイスのポイント

Slide 12

Slide 12 text

Monorepo 12 ● UI / BFF / Microservices を単一のGitHub Repositoryに共存 させるモノレポ構成を導入 ○ Repository間の移動によるコンテキストスイッチを軽減 ○ path-filteringにより、変更されたファイルのパスに応じてCIを制御 ● マイクロサービスの雛形を自動生成するスクリプトを用意 ○ 自動生成→CIに組み込むだけで開発環境にサーバーが立ち上がる ディレクトリ構成 ./ - .circleci - proto - rust - common - service-a - service-b - service-c - typescript - apps - ui - bff

Slide 13

Slide 13 text

Clean Architecture 13 ● ドメインロジックはDomain層に集約 ● Infra層に対してはtraitを経由して依存する Domain Usecase Infra Repository Trait Repository Impl ディレクトリ構成 service-a - app - domain - usecase - infra - db_dto - repository_impl - grpc - grpc_handler - grpc_convert - common - external_libs - error

Slide 14

Slide 14 text

ドメイン駆動設計 14 ● 境界付けられたコンテキストの内側に集約を定義 ○ 集約は1つまたは複数のエンティティから構成される ● データの更新は必ず集約単位で行う ○ それぞれの集約は、集約内部のデータの不変条件を担保 BOM(部品表)コンテキスト 集約 集約 見積コンテキスト 集約 集約 受注コンテキスト 集約 集約 発注コンテキスト 集約 集約

Slide 15

Slide 15 text

crateを細分化 15 ● crateは他の言語でいうパッケージやモジュールに相当 ● 1つのマイクロサービスの内部を多数の小さなcrateに分割 ○ ビルド時間短縮による開発者体験の向上 ○ リプレイス前は一部のcrateが肥大化し、ビルド時間のボトルネッ クとなっていた Domain Usecase A Usecase B gRPC Handler A Repository A Repository B App (Entrypoint) gRPC Handler B

Slide 16

Slide 16 text

テスタビリティ向上 16 ● mockallクレートでRepositoryのtraitをモック化し て単体テスト ○ リプレイス前は、データベースI/Oに独自の社内ライブラリを利用 していた関係で、Usecase層のテストが書きづらかった ● 結合テストのコードもRustで書いている ○ gRPCリクエストを飛ばすテストコード専用のcrateを定義 ○ cargo testで走らせる

Slide 17

Slide 17 text

苦労していること 17 ● ドメインモデリング ○ 事業規模拡大により、案件担当者、検品拠点スタッフ、会計担当者 など、ユーザーごとのニーズの多様性が増大 ○ 見込み発注、在庫引当、装置一式組立…… ● マイクロサービス ○ サービス間通信、分散トレーシング、サービスメッシュ…… ● テスト・QA ○ Rustの型の厳しさの代償として、テストコードを書くコストが重い ○ UIまで含めたe2eテストも自動化したいが、まだ手つかず

Slide 18

Slide 18 text

18 おわりに

Slide 19

Slide 19 text

We are hiring! 19 ● 以下のような方、一緒に開発しませんか? ○ 複雑な業務ドメインのモデリングが好きな方 ○ 高速で変化するビジネスモデルに対応できる柔軟なソフトウェア アーキテクチャを探求したい方 ○ モダンな技術をフル活用して事業価値を実現したい方 ● ぜひカジュアルにお話ししましょう ○ ご応募はこちらから: https://corp.caddi.jp/recruit/eng