Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
現実世界におけるスキーマ設計の妥協
Search
sadnessOjisan
April 25, 2023
Technology
21
29k
現実世界におけるスキーマ設計の妥協
sadnessOjisan
April 25, 2023
Tweet
Share
More Decks by sadnessOjisan
See All by sadnessOjisan
TypeScript、上達の瞬間
sadnessojisan
46
13k
フロントエンド・オブザーバビリティを支える要素技術を学ぼう
sadnessojisan
2
460
疎通2024
sadnessojisan
5
1.3k
BasicBasic認証
sadnessojisan
5
3.8k
ISUCON入門以前_ISUNARABE_LT#1
sadnessojisan
18
4.8k
サーバーとは何かを理解して、コンテナ1つで実行しよう | PHPerKaigi2024
sadnessojisan
34
14k
Node.js v12 を使い続けていたのはなぁぜなぁぜ?
sadnessojisan
11
26k
かにさんタワーバトル
sadnessojisan
1
23k
my new error
sadnessojisan
0
180
Other Decks in Technology
See All in Technology
Storybook との上手な向き合い方を考える
re_taro
2
330
BLADE: An Attempt to Automate Penetration Testing Using Autonomous AI Agents
bbrbbq
0
320
Python(PYNQ)がテーマのAMD主催のFPGAコンテストに参加してきた
iotengineer22
0
530
CysharpのOSS群から見るModern C#の現在地
neuecc
2
3.5k
Engineer Career Talk
lycorp_recruit_jp
0
190
Amazon CloudWatch Network Monitor のススメ
yuki_ink
1
210
データプロダクトの定義からはじめる、データコントラクト駆動なデータ基盤
chanyou0311
2
340
FlutterアプリにおけるSLI/SLOを用いたユーザー体験の可視化と計測基盤構築
ostk0069
0
110
ISUCONに強くなるかもしれない日々の過ごしかた/Findy ISUCON 2024-11-14
fujiwara3
8
880
SREが投資するAIOps ~ペアーズにおけるLLM for Developerへの取り組み~
takumiogawa
1
460
OTelCol_TailSampling_and_SpanMetrics
gumamon
1
220
10XにおけるData Contractの導入について: Data Contract事例共有会
10xinc
6
680
Featured
See All Featured
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
VelocityConf: Rendering Performance Case Studies
addyosmani
325
24k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
Navigating Team Friction
lara
183
14k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.3k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
16
2.1k
Side Projects
sachag
452
42k
For a Future-Friendly Web
brad_frost
175
9.4k
Ruby is Unlike a Banana
tanoku
97
11k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Transcript
現実世界における、 スキーマ設計の妥協 日本経済新聞社 Yuta Ide Encraft #2 サーバーとクライアントを結ぶ技術 1
自己紹介 module.exports = { name: “Yuta Ide”, belong: “日本経済新聞社”, role:
[“Client”, “Edge”, “BFF”], lang: [“TypeScript”, “Rust”].push(“Scala”), “schema now I’m using”: [“JSON Schema”, “zod”, “joi”, “GraphQL”, “pbuf”] } 2
3
羨ましい!!! 4
tl;dr スキーマレスに10年以上開発されているものに、 スキーマを導入するのは難しすぎる! 5
日本経済新聞社の負債返済活動 • 3ヶ月開発を止めて非機能要件開発 ◦ Node.jsアップデート作業 ◦ アラート改善活動 ◦ スキーマ改善活動 ◦
パフォーマンスメトリクス計測の仕組み ◦ 過去ページのアーカイブ化(SSG) ◦ E2E整備 https://speakerdeck.com/sadnessojisan/jian-shi-senaakansi-w u-da-zhi-dakeniookamitutena https://www.youtube.com/watch?v=L--AVk29m6w 6
スキーマ改善活動 手書きのAPIスキーマ定 義、実際の値と異なって いるから直しても良かで す??? わぁ、いいよ! ありがとう!!! 7 ※ここまで和やかな職場ではありません
スキーマ改善活動 • バックエンドチームを兼務してSpecやMiddlewareを書いていた • 手書きyaml の Spec から Validator を生成して、それを
Django (Python) に組み込む仕組みを作った • そして現実を知るのである、「Specが信用できないのはAPIチームのサボ タージュや技術不足ではなく、そもそも・・・」 スキーマ駆動開発導入の難しさを知る 8
スキーマ駆動開発とは 9
ここでの定義は、 フロントエンドとバックエンドで、 疎通のインターフェースを合意し、 そのインターフェースに従う形で お互いが開発すること。 10
スキーマの運用を疎かにすると何が起きるのか • サーバーの開発が終わるまでフロントは疎通できない • クライアントが想定していないデータを受け取った場合、クライアント側で ランタイムエラーが発生する(かもしれない) • サーバーからどういうデータが返ってくるのか、コンピュータは分からない 11 API開発
フロント開発 API改修 フロント対応 BE FE
反対にスキーマがあると何が嬉しいのか • サーバーサイドの開発を待たずにどういうレスポンスが返るか分かる • サーバーの戻り値の型を得られる • APIクライアントも得られる(作れる) 12 API開発 フロント開発
API改修 フロント対応 schema schema BE FE
スキーマ通りの値をサーバーが返すことが前提 スキーマは実値を表しているとは限らない!!!! 13
ドキュメント・バリデータ・型を同期させよ! https://blog.ojisan.io/swagger-validator-ts/ 14
フロントエンド開発における 代表的な技術 15
JSON Schema • スキーマ: JSON Schema • 型: typebox, json-schema-to-ts
• バリデーション: ajv 16
Swagger • スキーマ: Open API Spec • 型: swagger-typescript-api •
バリデーション: swagger-model-validator, ajv(OAS is based on json schema) 17
zod • スキーマ: zod • 型: z.infer<T> • バリデーション: z.parse()
18
GraphQL • スキーマ: GraphQL schema • 型: graphql-code-gen • バリデーション:
??? (graphql-codegen-typescript-validation-schema と いうのが最近あるらしいが筆者は触っていない) 19
gRPC • スキーマ: Protocol Buffers • 型: ts-protoc-gen, ts-proto •
バリデーション: ??? 20
バリデーションは本当に必要? 21
client API Data Source スキーマ駆動開発はここの話 22
client API Data Source 23 validation validation スキーマ駆動開発はここの話
client API Data Source 型や契約が満たされている処理 24
client API Data Source 型が付いている通信 型が付いてるのに、 validation 必要??? 25
バリデーション系の話をあまり聴かない • ajv はともかく、schema に対するバリデーションの話を聞かない ◦ ajv は逆で、バリデーションライブラリがあって、要求しているIDLがJSON Schemaという関 係
• unknown な箇所に validation して型をつけた後のレスポンス作成におい て、プログラミング言語が型安全を保証しているのであればvalidation は不 要では? 26
正しい、静的な型があるとは限らない • そもそも静的型付け言語でなければ型安全にならない • 動的型付け言語ではそもそもレスポンスの形に型付かないし、コードからド キュメントやSpecを生成できない(難しい) • 静的な型があったとしても、型をごまかすハッチの存在、型指定をうっかり 忘れるなど、多くの言語では型に対してウソをつける 相手が信用できないのなら、
スキーマ通りの値か検証した方が良い 27
コードから生成しないSpecは嘘をつける POST /users 絶対200 手書きSpec Wiki スプレッドシート なんでもあり実装 28
言語・FW選択で割と勝敗が決まる • コードからドキュメントやSpecを生成するのが苦手な言語やFWというもの は存在する • 静的型付け言語で書かれたコードなら、型情報を使ってレスポンスのSpecを 生成しやすい • もちろん自分で解析したらどの言語でもできるが、「そこまでできます か?」という問題がある
• 例: MVC FW からモデルを介さずにレスポンスを作る 29
言語・FWに依存しない スキーマ駆動開発をしたい! 30
JSON Schema という妥協 31
日本経済新聞社のアーキテクチャ CDN Origin API GW 13年間も積み重なった レガシー&入稿基幹サー ビス群 Frontend Teamの持ち物
信用できない データ 32 Swaggerとかが生まれる前から稼働してい るサービスなのだから仕方がない!
日本経済新聞社のアーキテクチャ CDN Origin API GW 13年間も積み重なった レガシー&入稿基幹サー ビス群 Frontend Teamの持ち物
信用できない データ 33 Gateway の責務は Gateway なので素通しする 信用できない データ
日本経済新聞社のアーキテクチャ CDN Origin API GW 13年間も積み重なった レガシー&入稿基幹サー ビス群 Frontend Teamの持ち物
信用できない データ 34 信用できない データ どちらかでバリデーションをしたい
日本経済新聞社のアーキテクチャ Origin API GW 信用できない データ 本当はここでバリデーションをして、信 用できるデータにしたい 35
日本経済新聞社のアーキテクチャ Origin API GW 信用できない データ 本当はここでバリデーションをして、信 用できるデータにしたい 36 一部APIにおいて、バリデーションに使うための
OAS準拠の仕様書が存在せず、サーバー側でバリデータを作れない。 (スプレッドシートにある)
日本経済新聞社のアーキテクチャ Origin API GW 信用できない データ (APIGWから見た)クライアント側でバ リデーションする 37
日本経済新聞社のアーキテクチャ Origin API GW 信用できない データ (APIGWから見た)クライアント側でバ リデーションする 38 クライアントサイドには実績ある型定義があるので、
それをスキーマとして使うことができる。
つまりクライアントが 勝手にスキーマを持つ 39
ところでそれはスキーマ駆動開発ですか? • クライアントが独自にスキーマを持ってしまうと、もうそれはスキーマ駆動 開発では無い • ただし、クライアントのスキーマをサーバーが参照して開発しているのであ ればスキーマ駆動開発もできる validation & typing
のためのスキーマを作るだけ 40
クライアントで完結する技術選定 • そりゃあGraphQLやらgRPCが理想ですよ・・・ • が、それらの導入には APIGW の開発が必要となり、現実的では無い • JS/TS前提のスキーマライブラリを考える ◦
joi ◦ yup ◦ zod ◦ ajv 41
クライアントで完結する技術選定 https://blog.ojisan.io/i-use-ajv-instead-of-zod/ 42
選ばれたのは JSON Schema でした • 2023年現在、一番良いのは zod だと思う • が、これまでの歴史上
joi -> yup -> zod とスキーマライブラリが乗り換えら れている • zod の次が出た時、zod ベースのソースはどうなるのか?スキーマはサー バーの根幹であり、そこが流行に振り回されていいのか??? • 長生きするIDLを採用したい -> JSON Schema 言語や流行に左右されないIDLを使おう 43
JSON Schema とは • JSON で定義する、JSONの形 • 詳しくは JSONとJSON Schemaを改
めて理解する 44
ajv とは • Another JSON Validator • JSON Schema で定義された
validator を作れる https://ajv.js.org/ 45
fastify との相性が良い • frontendチームの技術スタックは Fastify での自作SSR • Fastify は Ajv
が標準で組み込まれ ており、req/res を検証・シリアラ イズできる • APIを持つ場合のSpec生成に Swagger を使う後JSON Schema か らSwaggerを吐き出すプラグインが ある https://blog.ojisan.io/swagger-validator-ts/ 46 https://blog.ojisan.io/swagger-validator-ts/
スキーマ導入までの道筋 1. スキーマを生成する 2. バリデータを生成する 3. バリデーションに失敗した時のハンドリングをする 47
スキーマ導入までの道筋 1. スキーマを生成する 2. バリデータを生成する 3. バリデーションに失敗した時のハンドリングをする 48
クライアントサイドにどうやって Schema を作るか • 手書きされた API Spec から JSON Schema
を作る ◦ 手書きされたSpecが実値と乖離しすぎていて却下 • サーバーの実値から JSON Schema を生成する ◦ 動的なキー(やめろ!)やOptionalがあるので実値からだとスキーマの完成形が分からない • TypeScriptの型から JSON Schema を生成する ◦ クライアント側はすでに await res.json() as any as Type としていて稼働実績ある型を使っ ている。それが正しいのかはさておき・・・ 49
TypeScriptの型から JSON Schema を生成する • quicktypeやtypescript-json-schema • すでにクライアントサイドにある型から JSON Schemaを自動生成する。運用し
ている型なのである程度正しい実績もあ る。 • 実は Optional, Nullable, Undefined の 扱いが怪しい・・・ https://blog.ojisan.io/typescript-json-schema-ajv/ 50 https://blog.ojisan.io/add-nullable-to-json-schema/
1 API に 610 行のスキーマが生成される。 コードからの自動生成でしかスキーマを導入できない!
1 API に 610 行のスキーマが生成される。 コードからの自動生成でしかスキーマを導入できない! _人人人人人人人人_ > Over Fetching <  ̄Y^Y^Y^Y^Y^Y^Y^Y^ ̄
スキーマ導入までの道筋 1. スキーマを生成する 2. バリデータを生成する 3. バリデーションに失敗した時のハンドリングをする 53
バリデーションしたあとの値には型がついて欲しい User型がついて欲しい 54
ajv で型を付ける JSON Schemaに対応する型の Genericsを渡す。(出鱈目な 方を渡すとコンパイルエラーに なる) 型がつく 55
JSON Schema に対応した型を作る • 今回はTSからJSON Schema を生成し たので考えなくてもいいが、そうで無 い場合どうすればいいか •
TS first な JSON Schema を生成でき る IDL を使う ◦ typebox ◦ zod • json-schema-to-ts の型 Util https://blog.ojisan.io/ajv-to-type/ 56
スキーマ導入までの道筋 1. スキーマを生成する 2. バリデータを生成する 3. バリデーションに失敗した時のハンドリングをする 57
バリデーションに失敗すること前提で作る • そもそも “single source of truth” が存在していないところにスキーマを導 入するので、バリデーションがスキーマ通りに成功する訳が無い •
残念ながら、バリデーションに失敗した時にそこで例外を投げられない • 例:記事表示ページで、おすすめ記事一覧リストのデータがバリデーション 違反でした。例外を投げて記事全体を表示されなくなることが許されるの か? 58
バリデーションに失敗すること前提で作る • バリデーションに失敗したら型を無理やり付けて素通しさせる • ただしSentryなどに吐き出されるようにしておく 59
スキーマ違反を素通しさせて意味があるのか? • 「結局スキーマ違反な値にウソの型を与えてクライアントに返せば、クライア ント側でランタイムエラー起きますよね」「わかっとるわ!!!!」 • Validation Error を集計することが大切 • クライアントが期待するデータに対する
Validation Error を集めることで、 APIチームやさらにその裏側に「こういうデータを返してください」と言える API側の手書きスキーマを改善する サイクルを作るための第一手 60
もしゼロから作り直すならどうするか 61
日本経済新聞社のアーキテクチャ CDN Origin API GW 13年間も積み重なった レガシー&入稿基幹サー ビス群 Frontend Teamの持ち物
信用できない データ 62 信用できない データ どこかでバリデーションをしたい どこか一層に信頼できないデータ層が生まれると、その前段に信頼できないデータは伝 播していく。つまりスキーマ通りの値を返す仕組みを作るのであれば、入稿や DBのス キーマから正しいスキーマを伝播していく必要がある
日本経済新聞社のアーキテクチャ CDN Origin API GW 13年間も積み重なった レガシー&入稿基幹サー ビス群 Frontend Teamの持ち物
信用できない データ 63 信用できない データ どこかでバリデーションをしたい 信用できないデータソースがある状況で信用できるデータを返すようにしたいのであれ ば、結局はどこかの層でバリデーションは必要で、誰かが APIがエラーを返す覚悟で例 外を投げなければいけない
リプレイスする時に守る1つの鉄則 • スキーマやドキュメントに違反した 値を返してはいけない ◦ 外部に公開するAPIは必ずSpecを公開す る ◦ Spec違反の値を返さないように検証する •
防御的(!?)・予防的・攻撃的・契約 プログラミングの考え方を自チーム や自社に当てはめて戦略を考えよう ◦ 0ベースで開発する場合、違反するもの は落として欲しい 64 https://speakerdeck.com/twada/php-conference-2016
DBや入稿側から正しいスキーマを伝播させる • DBに面する入稿システムやマイクロサービスが、DBのスキーマを元に型安 全なAPIを作ると信頼を伝播させられる ◦ ORM ◦ Compile-time SQL Check
DBからクライアントまでを 型安全に繋ぐ技術を採用する 65
例: ORM • Entity とクエリのマッピング • DBからの値が Entity として型がつくようになる •
例) Prisma, TypeORM, Active Record, Django ORM… 66
例: sqlx • Compile-time checked queries • ORMではないが、コンパイル時にス キーマを検証し、DTOへのマッピン グまでもマクロで可能
• コンパイル時にDBに対してクエリが 走り、クエリの実行結果とRustの世 界で型検査できる https://github.com/launchbadge/sqlx 67
型安全な言語を利用する • 多くの言語は、GraphQLもgRPCも型やスキーマをごまかして使えてしまう ので、クライアントからするとクエリやメッセージ通りの値を受け取れる保 証がない • 自分がサーバーを実装するなら Rust で書きたい!!!!!!!!! •
Rustの一番好きな点は、「型を誤魔化すコストが高く、誤魔化すモチベー ションがなくなる」ところ。any や ts-ignore 的な抜け道が塞がれている。 Rustで書かれたサーバーはそれだけでフロントエンドエンジニア視点で信用 できて、過剰な防御をやめる動機になる!!! 68
っていうのは全部妄想です • まとめ①: スキーマレスで動いてしまってるものにスキーマを入れるのは難しいです。フル リプレイスは救いですが、現実的にフルリプレイスなんてものは簡単にできるわけがないの で、できるところからやっていきましょう。妥協するには JSON Schema が良いです •
まとめ②: しかし妥協するからにはコンピューターフレンドリーで自動化された運用フロー に期待できず、人間が運用フローを作らないといけないです • そんな運用フローを一緒に整備してくれる方を募集しています 「散々レガシーなこと話しておいて人が来ると思ってるの?」と思うかもしれませんが、どこの会 社にもレガシーはあるはずだし悪いことだとは思っていないです。むしろそういった過去の遺産 を、技術力や組織の力でどう未来に繋げていくかという仕事はクリエイティブで面白いと思います し、自分も楽しいです。面白そうと思った方はぜひカジュアル面談しましょう。もちろんモダン環 境(Nix, Rust, Scala)でひたすら楽しい開発ができる仕事もあります。是非カジュアル面談をしま しょう。 69