AI事業本部 DX本部の黒崎(@kuro_m88)です。
社外(取引先など)向けに作成する仕様書を自動生成する取り組みを試しているので、アイデアを紹介します。今回はSnowflakeで構築されているデータ分析基盤のテーブル定義の仕様書を例に紹介していきます。ツールはGoを使って開発しました。

テーブル定義書

仕様書の運用コストを減らしたい

DX本部でのプロジェクトは、開発環境を社内向けに別途用意することが大半なので、取引先に許可を取らないと何かを試すことができないということは今のところほとんどありません。
そのため、実際の開発フローとしては詳細な仕様書を書き起こしてから実装することはあまりなく、大まかな方針を決めてから軽く実装してみて、うまくいきそうであればそれを仕様としてまとめつつ実装することが多いです。
データベースのテーブル定義であれば、それらの定義はGitHub上で管理されており、リリース時に自動もしくは半自動で適用されるため、実運用されているものを正として扱うという考え方です。

DX系のプロジェクトのように協業のような要素を含むプロジェクトでは、成果物の確認の意味も含めて各種仕様書を定期的に提出するような場合もあります。ほかにも複数社が関係して協力して推進するものもあります。
欲を言えば、マイグレーションツールの定義ファイルやテーブル定義のSQLを仕様書としてしまいたいのですが、それだと誰もが読めるものではないのと、場合によってはフォーマットについて取り決めがあるかもしれません。
そのようなケースでは万能なExcelシートをつかって仕様書を管理することが多いと思いますが、DXを推進する部署なので、定義や仕様の二重管理は避けたいところです。自動生成しましょう🚀

データウェアハウスのテーブル定義を抽出する

Snowflakeに限った話ではないですが、データベースにはテーブル定義を表現するシステムテーブルがあることが多いです。SQLで抽出しましょう。
Snowflakeのテーブル定義抽出SQL
(このブログにSQLを直接書くとWAFにブロックされてしまうため、画像を貼っています。テキスト版はこちらに貼っておきます。)

“github.com/jmoiron/sqlx” を使えばSQLの実行結果が構造体にマッピングされるので、抽出結果も扱いやすいです。


type columnData struct {
	DBName       *string `db:"DB_NAME"`
	SchemaName   *string `db:"SCHEMA_NAME"`
	TableName    *string `db:"TABLE_NAME"`
	ColumnName   *string `db:"COLUMN_NAME"`
	DefaultValue *string `db:"DEFAULT_VALUE"`
	IsNullable   *string `db:"IS_NULLABLE"`
	DataType     *string `db:"DATA_TYPE"`
	Comment      *string `db:"COMMENT"`
}
.....
columns := []columnData{}
if err := db.Select(&columns, query); err != nil {
	log.Printf("failed to run a query. %v, err: %v", query, err)
	return nil, err
}

自動生成するためのテンプレートを用意する

抽出した情報を資料の形式にするためのテンプレートを作ります。長すぎるので、テーブル一覧のページのみ抜粋します。Goで開発しているので、標準ライブラリの”text/template”を使ってHTMLのテンプレートを書きました。ループを駆使して全ての情報が網羅されるように書いていきます。

<html lang="ja">                                                                                                                                                                                                                                                                 
<head>                                                                                                                                                                                                                                                                           
  <meta charset="utf-8">                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                 
  <title>{{ $g.Title }}</title>                                                                                                                                                                                                                                                  
  <style>                                                                                                                                                                                                                                                                        
    {{ $g.HTMLCSS }}                                                                                                                                                                                                                                                             
    @page { size: A4 }                                                                                                                                                                                                                                                           
  </style>                                                                                                                                                                                                                                                                       
</head>                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                 
<body class="A4">     
.....                                                                                                                                                                                                                                                           
  <!-- テーブル一覧 -->
  <section class="sheet table-list-page">
    <div class="row" id="row-1">       
      <h1>テーブル一覧</h1>
    </div>                   
    <div class="row" id="row-2">
      <table>                 
        <tr>                     
          <th>No.</th>           
          <th>テーブル名</th>    
          <th>備考</th>
        </tr>   
        {{ range $i, $t := $g.Tables -}}
        <tr>
          <td>{{ inc $i }}</td>
          <td>{{ $t.Name }}</td>
          <td>{{ $t.Comment }}</td>
        </tr>                                                       
        {{ end -}}                                                                                                                      
      </table>                                                                                                                                                                                                                                                                   
    </div>
  </section>
</body>
.....

「仕様書」っぽい見た目に仕上げる

素のhtmlだけだとこんな感じです。ここから「仕様書」っぽい見た目に仕上げていきましょう。
cssがない状態のhtml

paper-css

今回はPaper CSSというcssを利用させて頂きました。 https://github.com/cognitom/paper-css
このCSSを用いると改ページはもちろん、印刷に適したレイアウトでデザインすることができるようになります。今回はA4サイズで作ってみました。
通常だとCSS中にはpx, rem, em, %のような単位を使うことが多いですが、今回はあえてmmを使っています。紙を意識したレイアウトなので、相対的なレイアウトはもちろん、実寸大の寸法に合わせて座標が指定できるので、慣れると非常にデザインが楽です。

.title-page #row-1 {                 
  padding-top: 20mm;                     
  padding-bottom: 10mm;                
}

そのままだと、印刷時に塗りつぶしや背景が印刷されなかったので、”print-color-adjust: exact”を付与することで塗りつぶしも表現できるようにしました。

* {
  print-color-adjust: exact;                                                                                                                                                                                                                                                     
  -webkit-print-color-adjust: exact;
}

仕上がり

こんな感じに仕上げました。ブラウザで表示すると、実際にはページごとに縦に連続しているように見えます。box-shadowのおかげで書類っぽく見えていますね👏

生成された仕様書

テーブル定義書

印刷するとこれがそのままページごとに出力されますし、CIに組み込んでHTMLをPDF化するところまで自動化すれば、DBマイグレーションの定義が変わるとともに自動で資料の更新をすることができます。配布も楽そうです✌️

さいごに

資料作成を自動化するアイデアをご紹介しました。データベースのテーブル定義に限らず、自動化できる業務は探せば色々ありそうなので、できそうなところから自動化を推進していこうと思います。

サイバーエージェントのAI事業本部ではさまざまな事業領域のDXを推進しています。どんなことをしているのか話を聞いてみたい方はお気軽にお声がけください!

140兆円市場を、再発明する
https://www.cyberagent.co.jp/careers/special/retaildxrecruit2021/