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
フロントエンド・オブザーバビリティを支える要素技術を学ぼう
sadnessojisan
2
330
疎通2024
sadnessojisan
5
1.1k
BasicBasic認証
sadnessojisan
5
3.5k
ISUCON入門以前_ISUNARABE_LT#1
sadnessojisan
17
4.2k
サーバーとは何かを理解して、コンテナ1つで実行しよう | PHPerKaigi2024
sadnessojisan
34
13k
Node.js v12 を使い続けていたのはなぁぜなぁぜ?
sadnessojisan
11
26k
かにさんタワーバトル
sadnessojisan
1
22k
my new error
sadnessojisan
0
160
監視せなあかんし、五大紙だけにオオカミってな🐺🐺🐺🐺🐺
sadnessojisan
5
25k
Other Decks in Technology
See All in Technology
つよつよリーダーが 抜けたらどうする? 〜ナビタイムのAgile⽀援組織の変遷〜
navitimejapan
PRO
22
13k
HashHub会社案内「なぜ今、パブリックブロックチェーンに賭けるのか」
hashhub
3
75k
YAPC::Hakodateの映像記録を支える技術
godan
2
110
業務ヒアリングと知識の呪い
tamai_63
0
140
XP matsuri 2024 - 銀河英雄伝説に学ぶ
kawaguti
PRO
3
520
【shownet.conf_】放送局とShowNetが共創する、未来の放送システム ~Media over IP 特別企画の裏側~
shownet
PRO
0
270
スクラム導入の舞台裏:QAエンジニアがスクラムマスターになるまで
bubo1201
0
130
エムスリー全チーム紹介資料 / Introduction of M3 All Teams
m3_engineering
1
240
トークナイザー入門
payanotty
2
720
Authenticator のエミュレーションによる パスキーのログインテスト/nikkei-tech-talk-25
nikkei_engineer_recruiting
0
140
成果のためのコミュニケーション - 語彙を育てよう -/communication-for-good-outcome-developing-vocabulary
hassaku63
4
150
How CERN serves 1EB of data via FUSE
ennael
PRO
0
16k
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
41
9.2k
Designing with Data
zakiwarfel
98
5.1k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.2k
Done Done
chrislema
181
16k
Rails Girls Zürich Keynote
gr2m
93
13k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
23
1.7k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
230
17k
GraphQLの誤解/rethinking-graphql
sonatard
65
9.9k
Fireside Chat
paigeccino
32
2.9k
How to train your dragon (web standard)
notwaldorf
87
5.6k
Why Our Code Smells
bkeepers
PRO
334
57k
Ruby is Unlike a Banana
tanoku
96
11k
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