Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Blazor WASM × Code-first gRPC で始める C# ⼤統⼀理論

Blazor WASM × Code-first gRPC で始める C# ⼤統⼀理論

■イベント
イマドキのC# .NET Web開発 〜gRPC, GraphQL, Blazorもあるよ〜
https://sansan.connpass.com/event/316664/

■発表者
Sansan Engineering Unit Data Hubグループ
今村 有人

■ Sansan Data Hub エンジニア採用情報
https://media.sansan-engineering.com/datahub-engineer

SansanTech

May 15, 2024
Tweet

More Decks by SansanTech

Other Decks in Technology

Transcript

  1. 2 © Sansan, Inc. ⾃⼰紹介 2022年に中途で Sansan に⼊社。 Sansan Data

    Hub 開発メンバーとしてジョインし、現在で はチームリーダーを務めている。 メンバーの強みが発揮されるようマネージするのが得意。 最近は Sansan Data Hub の組織課題とプロダクト価値向上 の2軸に向き合っている。 今村 有⼈(Imamura Arito)
  2. 3 © Sansan, Inc. ※詳細は省略しています (各マイクロサービスのデータストア等) 本発表のスコープ 管理⽤画⾯ エンリッチ⽤ データソース

    データ書き出し先 データ取り込み元 データ連携⽤API エンリッチ処理群 書き出し処理群 取り込み処理群 コアデータ群 WASM + gRPC(※)の 部分を紹介 ※正確には gRPC-Web .NET GraphQL Client のリアル Blazor WASM x Code First gRPCで始める C# ⼤統⼀理論
  3. 4 © Sansan, Inc. 1. ユーザー向け管理⽤アプリの概要 2. Why Blazor WASM

    ? 3. Why gRPC-Web ? 4. Blazor WASM の⼯夫点 5. [まとめ] Blazor WASM、Code-first gRPC-Web を導⼊してみて アジェンダ
  4. 5 © Sansan, Inc. ユーザー向け管理⽤アプリの概要 - Sansan Data Hub 管理者ユーザ向け画⾯機能

    - 管理者向けにユーザー管理、セキュリティー管理などの管理機能を提供 - Blazor WASM + Code-first gRPC で作っている - 通信は gRPC-Web Client [Blazor WASM] BFF Backend Service [gRPC-Web] [gRPC-Web]
  5. 7 © Sansan, Inc. Why Blazor WASM? Sansan Data Hub

    では2つの Blazor のホスティングモデルを使い分けている Blazor WebAssembly Blazor Server
  6. 8 © Sansan, Inc. Blazor Server Blazor Server Why Blazor

    WASM? - アプリは ASP.NET Core で作られた サーバー内で動く - SignalR を介してブラウザと常時接続 される - 常に繋いでられるサーバーが必要 - Sansan Data Hub では開発内部で使う 運⽤アプリに採⽤ - 社内利⽤なのでネットワークにそこまで 敏感にならなくてもいい - 利⽤ユーザーは限られている - WASM に⽐べて直リンク時の挙動が軽い
  7. 9 © Sansan, Inc. Blazor WebAssembly Blazor WebAssembly (WASM) Why

    Blazor WASM? - アプリがブラウザですべて完結する - アプリ、Razor コンポーネント、その依存関係、 .NET ランタイムがブラウザにダウンロードされる - サーバーがなくてもスタンドアロンで動かせるし、 別にサーバー側は ASP.NET である必要はない - ダウンロードサイズは⼤きい - ユーザー向け管理⽤アプリに採⽤ - SignalR だとクライアントのネットワーク環境 に左右されやすいので Server ではなくWASM
  8. 10 © Sansan, Inc. Why gRPC-Web? - gRPC は Google

    が開発した RPC (Remote Procedure Call) のフレームワーク - gRPC-Web - ブラウザから gRPC を使うための仕組み - Sansan Data Hub で利⽤しているのは gRPC-Web - なぜ BFF - Backend Service 間も gRPC-Web ? > 設計当時は AppService が HTTP/2 に対応していなかったため Client (管理⽤画⾯) [Blazor WASM] BFF Backend Service [gRPC-Web] [gRPC-Web]
  9. 11 © Sansan, Inc. - .NET 5 から gRPC-Web が標準的にサポートされるようになった

    - Code-first gRPC を使うことで、.proto なしで C# だけでシームレスに gRPC-Web を使える - protobuf-net.Grpc - Sansan Data Hub の課題のひとつ (後述) も解消できそう Why gRPC-Web?
  10. 12 © Sansan, Inc. Why gRPC-Web? 「REST しんどい」 - 設計時の制約が⼤き過ぎる

    - エンドポイントを作る度サーバー、クライアントもケースに応じて HTTP 通信の設計を考えるのが⼤変 > サーバーもクライアントも各種ケースに応じて、返すステータスコードを考えて... > 400 だの 403 だのそれぞれ対応したステータス返して、クライアントも対応した ハンドリング⼀応書いて...とか⼤変じゃない? - RESTの表現 (GET、POST、DELETE…) だけで各ドメインのリソースに 対する操作を表すのつらすぎ > リソース指向と画⾯のユースケースが噛み合わないことがある > 単純な CRUD ならまだしも…
  11. 13 © Sansan, Inc. Code-first gRPC コード例: ドメインモデル「User」の Contracts public

    interface IUserService { [Operation] Task<Response<User>> GetAsync(GetRequest request, CancellationToken cancellationToken); [Operation] Task<Response<List<User>>> ListAsync(ListRequest request, CancellationToken cancellationToken); [Operation] Task<Response<User>> CreateAsync(CreateRequest request, CancellationToken cancellationToken); [Operation] Task<Response> ResetSettingsAsync(ResetSettingsRequest request, CancellationToken cancellationToken); } ・・・ ドメインモデルのインターフェースを先に設計・実装したものを Contracts として、 Nuget のプライベートリポジトリに公開する こうすることで、ドメインモデルのふるまいを C# で直接的に表現できる Code-first gRPC の恩恵 - ユースケースから洗い出した登場⼈物 のふるまいを、リソース指向の世界に 置き換えずに直接表現できる - Contracts を配布すれば「あと、実装す るだけ」に持っていける - クライアント-サーバー間のミスマッチ をなくせる
  12. 14 © Sansan, Inc. コード例: BackendService 側 public class UserService

    : IUserService { ・・・ public UserService(・・・) {・・・} public async Task<Response> ResetSettignsAsync(ResetSettingsRequest request, CancellationToken cancellationToken) { ・・・ var user = await _userDbContext.Users.Include(x => x.Settings).FirstOrDefaultAsync(・・・); ・・・ await _userDbContext.SaveChangesAsync(cancellationToken); } ・・・ public class UserSettingsService : IUserSettingsService { private readonly IUserService _userService; ・・・ public UserSettingsService (IUserService userService, ・・・) { _userService = userService; ・・・ } public async Task<Response> ResetSettingsAsync(ResetSettingsRequest request, CancellationToken cancellationToken = default) { var response = await _userService.ResetSettingsAsync(・・・) return new Response { Status = response.Status }; } ・・・ コード例: BFF 側 Code-first gRPC - サービスクラスのメソッドを呼び出す感覚で実装できて書き⼼地抜群 - ドメインモデルに対する操作も直感的 - グッバイ!!「POST backend_/iam/{tenantId}/user/authentications/totp/_reset」(※しまいには body に { true }だけ) みたいな わけわからんエンドポイント!!
  13. 15 © Sansan, Inc. Blazor WASM の⼯夫点: ロギング - 気にした点

    - Sentry や Application Insights の SDK をブラウザに⼊れてしまうとアクセス中のドメイン 以外の外部ドメインにアクセスしてしまう > エンプラ系のユーザーはアクセス可能なドメインのホワイトリストを運⽤しているケースがあり、 ログの送信に失敗するおそれがある - 都度ログをサーバに送信する作りだと、クライアントがオフライン状態で操作された場合、 送信に失敗してしまう - ⼯夫点 - 以下の仕組みを⾃前で作り込んでいる > ログをブラウザの LocalStorage に⼀定量ため込んで Log 記録⽤ API に送信する > オフラインなら LocalStorage にため続け再度オンラインになったら送信する
  14. 16 © Sansan, Inc. Blazor WASM の⼯夫点: バージョニング - 気にした点

    - 初回にアプリをクライアントにダウンロードさせる WASM なので、アプリのバージョン が上がった時に新しいアプリをどう提供するか?を考慮する必要がある - 利⽤中にアプリのバージョンが上がった時にどうリロードさせるか? - ⼯夫点 - アプリのバージョンが上がったタイミングでユーザにポップアップ通知をして、その上で リロードしないと使い続けられない仕組みをいれている
  15. 17 © Sansan, Inc. [まとめ] Blazor WASM、Code-first gRPC を導⼊してみた所感 -

    ポジティブ - フロントエンドから Backend まで、C# で⼀気通貫に書けるのはやっぱりよい > 開発組織とマッチしており学習コストやメンテナンスコストなど、 あらゆる場⾯で技術スタックの統⼀は⼤きい - ドメインモデルのふるまいを Code-first gRPC のおかげでシンプルに表現できる のもすばらしい - ネガティブ - たまにブラウザの DLL のダウンロードを制限しているユーザーもいる > でも、コミュニケーションするなどして運⽤の中で意外とカバーできる > .NET 8 では .dll ではなく .wasm ファイルでダウンロードできるようになっている