Slide 1

Slide 1 text

Blazor WASM × Code-first gRPC で始める C# ⼤統⼀理論 Sansan株式会社 今村 有⼈

Slide 2

Slide 2 text

2 © Sansan, Inc. ⾃⼰紹介 2022年に中途で Sansan に⼊社。 Sansan Data Hub 開発メンバーとしてジョインし、現在で はチームリーダーを務めている。 メンバーの強みが発揮されるようマネージするのが得意。 最近は Sansan Data Hub の組織課題とプロダクト価値向上 の2軸に向き合っている。 今村 有⼈(Imamura Arito)

Slide 3

Slide 3 text

3 © Sansan, Inc. ※詳細は省略しています (各マイクロサービスのデータストア等) 本発表のスコープ 管理⽤画⾯ エンリッチ⽤ データソース データ書き出し先 データ取り込み元 データ連携⽤API エンリッチ処理群 書き出し処理群 取り込み処理群 コアデータ群 WASM + gRPC(※)の 部分を紹介 ※正確には gRPC-Web .NET GraphQL Client のリアル Blazor WASM x Code First gRPCで始める C# ⼤統⼀理論

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

フロントエンドからバックエンドまで ⼀気通貫で C# で書けそう(書きたい)!!

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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]

Slide 11

Slide 11 text

11 © Sansan, Inc. - .NET 5 から gRPC-Web が標準的にサポートされるようになった - Code-first gRPC を使うことで、.proto なしで C# だけでシームレスに gRPC-Web を使える - protobuf-net.Grpc - Sansan Data Hub の課題のひとつ (後述) も解消できそう Why gRPC-Web?

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

14 © Sansan, Inc. コード例: BackendService 側 public class UserService : IUserService { ・・・ public UserService(・・・) {・・・} public async Task 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 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 }だけ) みたいな わけわからんエンドポイント!!

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

No content