Slide 1

Slide 1 text

TypeScriptとの歩み そしてtRPCのなにが嬉しいのか https://optimind.connpass.com/event/308598/ 発表時資料 (公開用) luma 2024-02-29 〜エンタープライズ向け開発での実例を添えて〜

Slide 2

Slide 2 text

チーム・プロダクト紹介(1/4) 以下のリリースも参照してください 新機能「Loogiaコネクト」をリリース 配車計画に必要な情報をシームレスに連携 複雑な条件を考慮し た計画作成もより簡単に~データ変換時間を大きく圧縮へ Loogiaコネクトというプロダクトを作っています. コネクト以前 Loogiaの最適化の提供を担っているのが 「計画作成」と現在は読んでいる部分. Loogiaといえば,これを指した.

Slide 3

Slide 3 text

チーム・プロダクト紹介(2/4) どんな商品があって どこに届けたくて 使える車両はどれで 稼働できるドライバーは誰で その他,どんな制約があるか Loogiaのパラメータ 最適化するためには当然パラメータが必要である. コネクト以前

Slide 4

Slide 4 text

チーム・プロダクト紹介(3/4) どんな商品があって どこに届けたくて 使える車両はどれで 稼働できるドライバーは誰で その他,どんな制約があるか Loogiaのパラメータ パラメータを用意するために,お客様自身でデータを用 意・加工・手作業で作成,もしくはオプティマインド側で 都度マクロを用意するなど... データが障壁になることは物流に限らずDXをしたいとき に立ちはだかる壁だろう. マクロでやるにしても課題が多い. 管理ができない,更新ができない,ノウハウがたまらない, 担当の人がマクロを扱えるかに依ってしまう,など. コネクト以前 お客さんの基幹データを手作業で加工してもらって... Excelでデータを毎回用意してもらって... この計画作成へのデータ投入,加工,並びに管理とお客様 のシステムとの連携全般をサービスとして提供するのがコ ネクトとなる.これにより,お客様のビジネスロジックを 汲むこともできる. マクロを誰かが用意して...

Slide 5

Slide 5 text

チーム・プロダクト紹介(4/4) どんな商品があって どこに届けたくて 使える車両はどれで 稼働できるドライバーは誰で その他,どんな制約があるか Loogiaのパラメータ コネクト 例: ● ドライバー情報,ドライバー のシフト情報 ● 商品データベース ● 毎日更新される注文情報 お客様の基幹データ 取り込み方法,下処理,パラメータへの変換 はビジネスロジック等を考慮した専用のデー タパイプラインを個社ごとに構築. ETLツール,Dataform,そして専門のデータ エンジニアをアウトソーシングして実現. 中間データをお客様が直接,閲覧・修正できる.ビジネスに合わせて 微修正するフローをサポートしたり,さらにLoogiaの計算結果を”利 用”して更にビジネス特化の最適化に役立てる運用も実現している.

Slide 6

Slide 6 text

チームメンバー構成 (2024-02-29時点) ● 開発6名 (フロントエンド,バックエンド,インフラ) ● QAは外部に委託 ● データエンジニアも外部に委託 ● PM 等は除いています.

Slide 7

Slide 7 text

プロダクト要件の概要 ざっくりどういう要件のWebアプリケーションか,だけわかるように紹介します. Web クライアント Firebase Authentication (認証・全社共通) Cloud Run (サービスAPI) Cloud Run Jobs (バッチ処理) データベース ● 基本は普通にWebアプリ ● リクエスト量がさほど集中す ることはない ● 長時間かかる処理があり,そ れはJobsに退避 ● バッチ処理も同じリポジト リ・コードで管理して,TSで 書かれている

Slide 8

Slide 8 text

作るうえで技術的に達成したかったこと ● JS/TSでのある程度の開発ができる人が自然にフロントエンド・バックエ ンド両方の開発を,すぐにできる仕組み ○ 時間もタイトな中でチームをすぐに作る必要があった ○ 専属マネージャーがいるわけでもなく,FE・BE・インフラそれぞれ専 属で多くのメンバーを一気に抱えられるわけでもなかった.コストも 潤沢な訳では無い. ○ 逆に,APIを外部に提供する必要や,高度な非機能要件はない.わりと 業務システムより. ● メンバーの力量やヒューマンエラーしないことに依存しないコード品質を 一定水準以上に保つ仕組み ○ 誰が書いても最低限の要請を自動で満たすようなセットアップが必要 ○ レビュー,QA等に取れる時間も限られた中で,いかに「ヒューマンエ ラー」を事前に発生させないか

Slide 9

Slide 9 text

TypeScriptの歴史を振り返る TSとともに生きてきた視点を紹介していき,どのようにtRPCにつ ながるのか紹介する.

Slide 10

Slide 10 text

2014 v1.1 v1.3 2015 v1.4 v1.5 v1.7 v1.6 Union Types (和型) T | U JSX ユーザ定義型ガード f unction (v ): v is T asy nc/await 2016 v1.8 v2.0 v2.1 Stringリテラル型 コントロールフロー解析強化 (デッドコード検出など) --strictNullChecks if (obj.f oo) { … } 系タイプガードの強化 nev er型 tagged unionについてはTypeScriptから構 造体を奪って、関数だけでまた構造体を得 るまでの道のり(チャーチ・エンコーディン グ) も見てね key of 型演算子 mapped ty pes 2017 v2.2 v2.3 v2.6 v2.4 v2.5 object型 Tagged Union Ty pes (直和型)

Slide 11

Slide 11 text

2018 v2.7 v2.8 v3.2 再帰型は以前までもテクニックを使 えばできたが,より直接的にできる ようになった v2.9 v3.0 Conditional Ty pes: T extends U ? X : Y bigint v3.1 2019 v3.3 v3.4 v3.7 v3.5 v3.6 Recursiv e Ty pe 2020 v3.8 v3.9 v4.0 v4.1 2021 v4.2 v4.3 v4.4 v4.5 Template Literal Ty pes

Slide 12

Slide 12 text

2022 v4.6 v4.7 v4.8 v4.9 2023 v5.0 v5.1 v5.2 v5.3 ... package.json の exports など 共変・反変の指定子 in/out unknownが実質 {} | null | undef ined 扱いに satisf ies演算子 using演算子 TypeScript Origins: The Documentaryやリリースノートもチェックしてみてください.

Slide 13

Slide 13 text

TypeScriptとの歩み ● 最初はちょっと便利な・修正されたJavaScript ● 型システムとして必要な解析力と表現力を備えていく ● ECMAScriptの先導者(仕様を主導する)から,ECMAScriptの先駆者(決ま りそうな仕様を先取りして実装)へ ● 実践への活用と積み重ねの繰り返し.今ではやっとnpmでパッケージを探せ ば大抵TypeScript定義も付いてくる時代に ● DefinitelyTyped,Microsoft自身によるTypeScript及びVSCode開発への利用に よるドッグフーディング ● 静的解析器として強力に.開発環境として便利に.型システムとして独自の 類をみない進化.

Slide 14

Slide 14 text

TypeScriptの型システム ● 漸進的型付けの部分的な採用 ○ やはりベースはちょっとずつJSから移行する,ちょっとずつ整備し ていくという発想 ● 二階の型システム (System F) 相当.これ自体はそこまで強力というわ けではなく,HaskellやScalaなどには劣る ● 重要なBreakthrough: ○ 型システム上での計算がチューリング完全に ■ Tagged Union (型での構造の直接的な表現力) ■ Conditional Types と infer (matchに相当) ■ Recursive Types (ループ・再帰に相当) ○ Template Literal Types により文字列リテラル型がパース可能に チューリング完全な型システムが必ずしもいいということにはならない 決定可能であるべきという前提をつけるのが型理論では基本になるし,例えばOCaml/Haskell/Coqなどがそう.

Slide 15

Slide 15 text

TypeScriptのネイティブランタイムの台頭 ● DenoやBunが登場 ● これらは型情報を落として実行するというのが基本なので,基本的には JSランタイムにほかならない.なので,大事なのはTSを通した体験が どうかということ. ● これらは実際とても体験がよい.DenoもBunも .ts ファイルを直接実行 の対象とすることができて,DenoはGo言語のようなURLインポートで node_modulesいらず.Bunは高速なnpm installの実現.いずれにしても ts-node とそれのための煩わしい設定がいらないのは大きな変化

Slide 16

Slide 16 text

2020年のあの日、TypeScriptに欲しかったもの 当時の前提: Node.jsがあってWebのUIもバックエンドもJavaScriptで書け る.当然,TypeScriptでかける.

Slide 17

Slide 17 text

(2020年) 深い溝 ただ,リモートにあるTSの 関数を呼び出したいだけな のに... 当時の選択肢: ● 一個一個の関数をうまくつなげる ○ fetch(...) as MyResponseType する ● OpenAPIで中間のスキーマをまた別で定義してコード生成 ● GraphQL前提でApollo + Nexus とコード生成 究極のDRYとSSOTを実現したいというかなんというか,これらはまだ完璧ではなかった. クライアント側でGraphQLを書くのとコード生成を許容すれば Apollo + Nexusはわるくなかったけどね

Slide 18

Slide 18 text

(2020年) 当時の最先端 つなぎたいって話 2020年夏ぐらいにたまたま見つけた frourio がこれへの解決を提示する最 先端にいた. ↑見つけたあとにコントリビュートしたり新たな職探しを始めたのがきっかけで転職 つながるって話 上の話をだいたい そのままできる frourioはaspidaというものと組み合わせて,コード生成で 補助しながら,関数を書いて呼び出すだけ,という体験 を提供しようとしたフレームワーク. きっとみんなこの発想はあったが,早期に実現していた のはfrourioだった.

Slide 19

Slide 19 text

そしてtRPCへ つなぎたいって話 tRPCの最初の方のリリース.ちょうどfrourioのあとにこれ系が栄え始めた印象 → https://trpc.io/

Slide 20

Slide 20 text

tRPCの概念概略図 router router [...] subscription procedure middlewareの合成 JSON→ JSON async ジェネレータ handler routerとclientを つなぐ ツリー構造を成す まさにつなげてくれてるやつ...! ↓HTTPによる実現が基本 しかし,それに限らないの client ↑ async関数として,React Queryとし て,などなど GraphQLとRESTの融合みたいな抽象化だ! query JSON→JSON async関数 middleware contextの変換

Slide 21

Slide 21 text

tRPCがもたらすもの 「async関数定義してクライアントで使う」を 「いい感じに繋いでもらって体験よく実現してほしい」 これを,「GraphQLとRESTの融合のような抽象化」 と「それをサポートするhandlerやライブラリの提供」 により実現する. そして,このあと説明するが,ContextとMiddlewareの 設計も体験がとてもいい.

Slide 22

Slide 22 text

tRPCのプロシージャの例 実際にプロジェクトでの例をかるーく編集したものを 紹介します.ほぼそのままです. 入力のないasync関数で, 検証済みトークン(ユーザーに 紐づく,ユーザーは必ず組織に所属する)がコンテキスト 上になければ no-session, 登録済みじゃなければ not- registered,登録済みの組織なら registered と返します. procedure とは 「JSONを受け取りJSONを返すasync関 数」が基本. さらに,コンテキストを横からもらうことができる(後で 説明)

Slide 23

Slide 23 text

tRPCのプロシージャの例 セットアップを済ませると,プロシージャを 増やすたびに型がうまく計算されて,エディ タで補完されるようになります. 型の計算でうまくやっているため,コード生 成も必要ありません. procedureの引数や返り値のJSON型も正しく 型がついている状態になります.

Slide 24

Slide 24 text

寄り道: ちょっとzodの紹介 ところで,procedureの入出力のJSON型は zodを利用して定義できます. 例えば先程の getUserInfo の返り値の型は左 のような形で定義されています. JSですからランタイムで利用できて,ランタ イムで「実際にそのスキーマを満たすかチェ ック」することができます. それと同時に,TSの対応する型を計算するこ とが型システム上で可能です.これは型を単 に書くだけでは実現できません.なぜなら, 型はランタイムに影響を与えることはない, というTypeScriptの根底があるためです.

Slide 25

Slide 25 text

寄り道: ちょっとzodの紹介 zodのinferを使えば,スキーマから型が得られる.

Slide 26

Slide 26 text

寄り道: ちょっとzodの紹介 zodを使うと気軽に「離れた場所とTypeScriptでつながる」ことができる ● Cloud Run と Cloud Run Jobs を結ぶところでも使っている.要は,単な るstringでやり取りしなければならないので,JSON.parseする.その時 にunknown相当を as T するのではなく,安全にfail-fastにパースする. ○ メンバーの書いた 業務でCloudRun Jobsを使ってみた話 も参照して みてください ● tRPCもzodの「離れた場所とTypeScriptでつながる」の例だ.ブラウザと サーバーという,TypeScriptで書けるけど離れた場所にある世界をつなぐ 存在

Slide 27

Slide 27 text

tRPCのmiddlewareとcontext まずは例から見ていきましょう. 左記は事前に用意したコンテキスト内のロール判定を もとに,ログイン済みでなければ401 Unauthorizedを 投げ,新たに,必ず検証済みトークンが入っているコ ンテキストを作成します. これで, userProcedure で作られたクエリはすべて認 証済みのユーザーしか到達しません. 新たにコンテキストを作成,といいつつコンテキスト は詰替えをしているだけに見えるかもしれません.し かし,これに大きな意味があります.(後述) 最後に,プロシージャ(のルートを作成する関数)に ミドルウェアを合成します.当然,何個でもミドルウ ェアを合成できます. ログイン済みユーザーのみ使えるプロシージャを作ることを考えます.

Slide 28

Slide 28 text

tRPCのmiddlewareとcontext isUserRoleの入力に対して出力のcontextの型が変わっているのがわかります. 出力.nullチェックをしたのでnullは消えている. 入力

Slide 29

Slide 29 text

tRPCのmiddlewareとcontext userProcedureで作ったqueryはコンテキストの中に認証済 みトークンが必ずある,という型付けになっている.

Slide 30

Slide 30 text

tRPCのmiddlewareとcontext publicProcedureには認証されていないユーザーも到達しうるので,ユー ザーに関する情報がnullかもしれない,という型付けになっている. なので,以下のように書き換えるとエラーに.

Slide 31

Slide 31 text

tRPCのmiddlewareとcontext 整理します. ● queryはJSONを受け取りJSONを返すasync関数.ただし,contextを 別で利用できる. ● contextはqueryを定義したprocedureにより決まる. ● procedureは0個以上のmiddlewareの合成である. ● middlewareはcontextを受け取りcontextを返すasync関数 これはかなりえらい抽象化...!

Slide 32

Slide 32 text

tRPCがもたらすもの tRPCはただのTypeScriptやAPIフレームワークではなく,JSON用RPCを 抽象化し,ミドルウェアもきれいに抽象化,型で表現. 従来のミドルウェアの,「なんか事前に処理するやつ」,というレベルの ものを,「型レベルで何をするのか」,という捉え直しをした. contextのこの抽象化が個人的にかなり好き

Slide 33

Slide 33 text

tRPC contextのチームでの利用例 ● データベースクライアント ● ロガー ● 認証コンテキスト(検証済みのトークンと,サービスでの解釈)

Slide 34

Slide 34 text

tRPCのその他のトピック ● subscription (websocketなどで実現されるリアルタイムな配信) ● テスト ● 開発用ツール (trpc-panelなど) ● エラーハンドリング・エラーフォーマッティング などなど

Slide 35

Slide 35 text

選択肢のおさらいとその他 ● OpenAPI とコード生成 ● GraphQLとApollo + Nexus (コード生成) ● Frourio + Aspida ● tRPC ● Zodios ● ElysiaJS + Eden (Bun) ● ts-rest ● Hono RPC 新時代...!

Slide 36

Slide 36 text

まとめ: 課題はどのように達成された? ● JS/TSでのある程度の開発ができる人が自然にフロントエンド・バックエンド 両方の開発を,すぐにできる仕組み ○ Node.jsを使用すればTSでバックエンドもかける.さらに tRPC を利用す ることでそのギャップを最小限にする.フロントエンドを書くようにバ ックエンドをかけるし,逆も同様. ● メンバーの力量やヒューマンエラーしないことに依存しないコード品質を一 定水準以上に保つ仕組み ○ CIやESLintの設定により徹底してルール準拠,さらに tRPC や zod によ り手動で外の世界と繋ぐというのをなくし,フレームワークで対処.人 やスタイルに依らない仕組み作りが可能. ○ CIが通ればCDも通る.レビューではロジックを見ることに集中できる.

Slide 37

Slide 37 text

世界のラストワンマイルを共に最適化する 仲間を探しています ご清聴ありがとうございました ©︎ 2024 OPTIMIND Inc. 採用サイト