Slide 1

Slide 1 text

CONFIDENTIAL
 NEWT Tech Talk TypeScript Compiler APIを利⽤した データベースドキュメントの⾃動⽣成 Backend Engineer 中野 紘典

Slide 2

Slide 2 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 2
 Backend Engineer 中野 紘典 ⾃⼰紹介 Background 2021年にヤフー株式会社(現LINEヤフー株式会社)に新卒⼊社。 社内オブジェクトストレージの開発/運⽤業務を担当。 2024年1⽉より令和トラベルにバックエンドエンジニアとして⼊社。 海外旅⾏アプリ「NEWT」のAPI開発と社内の業務⾃動化基盤の開発 に従事。 Tech ● TypeScript(令和トラベルに転職〜) ● Go(前職で使ってました) Like ● バスケ🏀 ● サウナ🧖 ● 旅⾏✈

Slide 3

Slide 3 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 Agenda 3
 ● 今までのDBドキュメントの運⽤⽅法と課題 ● 今回導⼊したDBドキュメント⾃動⽣成 ● どのように移⾏したか ● 導⼊した結果と今後

Slide 4

Slide 4 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 今までのDBドキュメント の運⽤⽅法と課題

Slide 5

Slide 5 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 DBドキュメントがなぜ令和トラベルで必要なのか? 5
 DBをエンジニア以外の社内の様々な関係者が利⽤するから 例) マーケティングチームの分析メンバーが予約状況可視化のダッシュボード作成 DBのテーブル定義やテーブルの関係をエンジニア以外も理解できる必要がある ツアーの⽬的地集計したいけど ⽬的地のカラムどれだ。。。 😇

Slide 6

Slide 6 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 Schema Spyを使⽤してDB情報を可視化 6
 Schema Spyとは? DBからエンティティ関係図を含む HTML ドキュメントを⽣成するツール エンティティ関係図 カラムの情報 SchemaSpyを使⽤してエンジニア以外のメンバーにもDBの情報を公開 → エンジニア以外のメンバーもテーブル定義やテーブルの関係を理解可能

Slide 7

Slide 7 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 エンジニア DBのドキュメントを反映する負担が増加 😇 7
 エンジニア以外のメンバー ドキュメントあるのでDBを理解しやすい👏 エンジニアから⾒たら課題が。。。

Slide 8

Slide 8 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 SchemaSpyへのドキュメントの反映⽅法 8
 1. 説明を記載したマークダウンファイル(mdファイル)を記述 2. mdファイルからXMLファイルに変換し、SchmaSpyへ反映するScriptを実行 (SchemaSpyのメタ情報の記載に則したXMLファイルを記載する必要がある) # sample サンプルの説明 ## id idの説明 ## column1 column1の説明 ドキュメント(md) SchemaSpyのメタ情報の記載に則したXMLファイル

Slide 9

Slide 9 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 従来の⽅法の課題 9
 ソースコードとテーブル定義ドキュメントが別ファイルとして管理 import {BaseEntity, Column, Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Sample extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() column1: number; } ソースコード # sample サンプルの説明 ## id idの説明 ## column1 column1の説明 ドキュメント(md)

Slide 10

Slide 10 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 従来の⽅法の課題 10
 レビューお願いします テーブル定義のmd記載してください 👍 すいません、記載します🙏 ドキュメント(md)がソースコードと離れていることからドキュメントを作成し忘れる 👉 レビューの⼯数が増える 👉 そもそもレビュワーも気づかずドキュメントが作成されないケースも... レビュワー

Slide 11

Slide 11 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 課題を解決するために必要なこと 11
 1. コメントとソースファイルを⼀箇所にまとめる🔥 → コメントを書きやすい状態にすることでエンジニアの負荷を減らす 2. エンジニアにコメントを追加することのメリットを作る🔥 → エンジニアがコメントにメリットを感じればコメントを書くはず

Slide 12

Slide 12 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 今回導⼊した DBドキュメント⾃動⽣成

Slide 13

Slide 13 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 今回導⼊した⽅法 👉ソースファイルにJSDoc  でコメントを記載 13
 👉TypeScript Compiler APIを 使っ  てソースファイルから JSDocを抽出し、XMLファイルを 作成 今まで これから マークダウンでコメント 定義のファイルを作成 マークダウンファイルから XMLファイルを作成 コメントの記載方法 SchemaSpyへの反映⽅法

Slide 14

Slide 14 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocとは? 14
 /** * Represents a book. */ function Book(title) { } ● ドキュメント化されるコードの直前に配置 ● /** で始める JSのソースコードに注釈を追加するために使われるマークアップ⾔語 JSDocのルール

Slide 15

Slide 15 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocを導⼊した背景 JSDocでコメントを記載することでエディター上でホバー形式で出⼒ 15
 コメントを書くことでエンジニアがソースコード上で参照できる→ エンジ ニア側もコメントを書くことにメリットが生まれる

Slide 16

Slide 16 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 TypeScript Compiler APIとは? TSのコードをプログラムで解析‧操作できるようにするためのAPI ソースコードを変換したASTを元に解析処理が可能 let message = "Hello"; console.log(message); ソースコード SyntaxList: let message = "Hello";console.log(message); FirstStatement: let message = "Hello"; VariableDeclarationList: let message = "Hello" LetKeyword: let SyntaxList: message = "Hello" VariableDeclaration: message = "Hello" Identifier: message FirstAssignment: = StringLiteral: "Hello" SemicolonToken: ; ExpressionStatement: console.log(message); CallExpression: console.log(message) PropertyAccessExpression: console.log Identifier: console DotToken: . Identifier: log OpenParenToken: ( SyntaxList: message Identifier: message CloseParenToken: ) SemicolonToken: ; EndOfFileToken: AST 変換 16


Slide 17

Slide 17 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 TypeScript Compiler APIを使⽤した背景 ソースファイルを解析してJSDocを抽出するコードをTypeScript Compiler APIなら簡単に実装可能 JSDocだけでなく@Entityなどの情報もAPIを通して取得できるので ソースファイルの特定も容易にできた 17
 import {BaseEntity, Column, Entity, PrimaryGeneratedColumn} from "typeorm"; /** * サンプルエンティティの説明 */ @Entity() export class Sample extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() column1: number; } 変換 SyntaxList: …. ImportDeclaration: ClassDeclaration: … JSDocComment: /** * サンプルエンティティの説明 */ ….

Slide 18

Slide 18 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 どのように移⾏したか

Slide 19

Slide 19 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 移⾏前〜 ⽣成AIで軽く試す 19
 TypeScript Compiler API は知見が全くなかったので 簡単なJSDocを記載したサンプルで生成AIを使って試してみた 👉TypeScript Compiler APIの使い方と簡単に実現できるか調べるため TypeScript Compiler API を使えば、以下が簡単に実装できると分かった💪 1. @Entityデコレータの検出   👉 ソースファイルの特定が可能だから 2. JSDocを検出&値を取得すること   👉 ソースファイルからコメントを抽出することが可能だから

Slide 20

Slide 20 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 どのように移⾏するか 20
 フェーズ1 mdファイルから該当するソー スファイルにJSDoc コメントを転記する フェーズ2 JSDocからSchemaSpyの メタデータの形式に則した XMLファイルを作成する

Slide 21

Slide 21 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 どのように移⾏するか 21
 フェーズ1 マークダウンのドキュメン トファイルから該当する ソースファイルにJSDoc コメントを転記する フェーズ2 JSDocからSchemaSpyの メタデータの形式に則した XMLファイルを作成する

Slide 22

Slide 22 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 マークダウンファイルからJSDocへの変換 - 全体図 22
 JSDocコメントの⽣成 TypeScriptファイルへJSDocを適⽤ mdファイルの検出 とパース

Slide 23

Slide 23 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 マークダウンファイルからJSDocへの変換 - マークダウンファイルの検出とパース処理 23
 # sample サンプルの説明 ## id idの説明 ## column1 column1の説明 mdファイル 各⾏は特定のパターンに基づいて処理 ● # で始まる⾏はテーブル定義として認識され、直後の⾏はそのテーブルの説明⽂として保存 ● ## で始まる⾏はカラム定義として認識同様に、直後の⾏はそのカラムの説明⽂として保存

Slide 24

Slide 24 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 マークダウンファイルからJSDocへの変換 - JSDocコメントの⽣成 24
 { title: "sample", content: ["サンプルの説明"], subSections: [ { title: "id", content: ["idの説明"] }, { title: "column1", content: ["column1の説明"] } ] } { // テーブル用のJSDoc classJSDoc: "/**\n * サンプルの説明\n */", // カラム用のJSDoc columnJSDocs: { id: "/**\n * idの説明\n */", column1: "/**\n * column1の説明\n */" } } mdのパース処理で作成した中間データをJSDocコメントに変換 ● メインセクションの説明⽂をテーブルの説明としてJSDocコメントに変換 ● サブセクションの説明⽂を各カラムの説明として形式のJSDocコメントに変換し、キー名と紐付け mdのパース処理で作成した中間データ JSDocに変換した中間データ

Slide 25

Slide 25 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 マークダウンファイルからJSDocへの変換 - TypeScriptファイルへJSDocを適⽤ 25
 /** * サンプルの説明 */ @Entity() export class Sample extends BaseEntity { /** * idの説明 */ @PrimaryGeneratedColumn() id: number; /** * column1の説明 */ @Column() column1: number; TypeScript Compiler APIを使⽤し以下ロジックでJSDocをソースファイルに追加 ● @Entityがあるクラスの場合 その上にテーブルの説明を追加 ● @PrimaryGeneratedColumn、@Columnの場合、カラムの説明を追加

Slide 26

Slide 26 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 どのように移⾏するか 26
 フェーズ1 マークダウンのドキュメント ファイルから該当するEnソー スファイルにJSDoc コメントを転記する フェーズ2 JSDocからSchemaSpyの メタデータの形式に則した XMLファイルを作成する

Slide 27

Slide 27 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocからSchemaSpyのXMLファイル変換 - 全体図 27
 ファイル検索 エンティティ解析 ドキュメント⽣成

Slide 28

Slide 28 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocからSchemaSpyのXMLファイル変換 - ファイル検索 28
 DB名に基づいて解析対象のTSファイルを特定 ● コマンドライン引数からDB名を取得し、そのDBに対応するモジュールディレクトリを決定 // 対象ファイルリスト const entityFiles = [ "src/modules/sample1/sample1. entity.ts", "src/modules/sample2/sample2. entity.ts" ]

Slide 29

Slide 29 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocからSchemaSpyのXMLファイル変換 - エンティティ解析 29
 /** * sampleの説明 */ @Entity() export class Sample extends BaseEntity { /** * idの説明 */ @PrimaryGeneratedColumn() id: number; /** * column1の説明 */ @Column() column1: number; // EntityInfo構造体 const entityInfo = { name: "sample", description: "sampleの説明", properties: [ { name: "id", type: "number", description: "idの説明" }, { name: "column1", type: "number", description: "column1の説明" } ] } 解析するtsファイル 解析後作成された中間データ

Slide 30

Slide 30 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 JSDocからSchemaSpyのXMLファイル変換 - ドキュメント⽣成 30
 // EntityInfo構造体 const entityInfo = { name: "sample", description: "sampleの説明", properties: [ { name: "id", type: "number", description: "idの説明" }, { name: "column1", type: "number", description: "column1の説明" } ] } XMLファイル 抽出したエンティティ情報をSchemaSpy形式のXMLファイルに変換 SchemaSpy互換のXMLファイルとして出⼒することで、DBドキュメントが⾃動⽣成

Slide 31

Slide 31 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 導⼊した結果と今後

Slide 32

Slide 32 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 導⼊した結果 32
 mdファイル(全64ファイル)に記載されていたテーブル定義を全てJSDocのコメントに変換できた🎉 ● JSDocでコメントをつけたことでホバー機能で詳細を確認可能 👉 ソースファイルとコメントが一箇所になった 👉 JSDocがつくことでエンジニアにもコメントを書くメリットが生まれた

Slide 33

Slide 33 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 @Field(() => Boolean, { description: `column1の説明`, }) /**   column1の説明 */ @Column({ type: "boolean", default: false }) column1: boolean; 苦労した点 33
 コメント追加が意図しない箇所にされてしまう 本来は@Fieldの上に配置したい。。。😇 現在チームで規定されている型と異なるパターンが存在 例)@Columnデコレータの上に別のデコレータ ソースファイルの書き方が統一されている 👉解析処理の観点でも大事 問題点 原因

Slide 34

Slide 34 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 今後の課題 34
 マークダウンのコメント定義にないテーブル、またはカラムはいまだに JSDocもなく、SchemaSpyのコメントとしても反映できていない ● GithubActionsでPR時にEntityファイルにJSDocの記載があるか チェックする機能を導⼊ ● モジュールごとにコメントの記載率を可視化 ⽬指せ、JSDoc記載率100%🔥

Slide 35

Slide 35 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 今回の学びと今後の展望 35
 今回の実装でTypeScript Compiler APIを使用すれば、ts ファイルを解析処理が容易にできることがわかった ライブラリの変更やアップデートで多くファイルの表記を変更す る場合などに応用できそうなのでやってみたい🔥 今回の学び 今後の展望

Slide 36

Slide 36 text

CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.
 まとめ 36
 SchemaSpyを使⽤してエンジニア以外のメンバーにもDBの情報を公開していた ソースコードとテーブル定義ドキュメントが別ファイルとして管理しているため、 反映作業がエンジニアの負担になっていた ● コメント定義⽤のmdファイルを作成 →ソースファイルにJSDocでコメントを追加 ● ソースファイルをTypeScript Compiler APIで解析し、XMLファイルを作成 ソースファイルにJSDocでコメントを書けるようになることで エンジニアがコメントを反映する負担が減るだけでなく、書くメリットが⽣まれた 問題 実装 結果

Slide 37

Slide 37 text

Thank you!
 CONFIDENTIAL
 © 2023 Reiwa Travel, Inc.