DBML、便利ですよね。

開発初期の段階でER図の共有やSQLのエクスポートなどで非常に役立つツールです。

しかし、それは開発初期の話。

開発が進むにつれて実装とDBMLに齟齬が生じてしまうため、メンテナンスがされなくなっていってしまいます。。

今回は、DBMLの管理をローカルから行うことが可能なdbdocsというツールを見つけたので、紹介したいと思います。

dbdocs.io - Database Documentation and Catalog Tool

使い方

早速インストールしてみます。

npm install -g dbdocs

(余談ですが、僕の環境ではnodenvを使用していたんですが、この-gオプションでインストールしたパッケージがうまく動かなかったので直したときの記事もあるので良ければこちらもどうぞ↓)

nodenvのnpmでインストールしたyarnからglobal addで追加したパッケージが使用できなかったときの対策

無事インストールできたら、まずはdbdocsにログインしましょう。

dbdocs login

Choose a login methodと表示されたので、Githubを選択するとWebブラウザに遷移するので、そこでログインを行うとトークンが発行されます。

発行されたトークンをターミナルに貼り付ければログイン完了です。

では次にdbmlファイルを実装します。

今回は簡易的にDBエンジンとしてPostgresを使用し、ユーザーテーブルのみがあるデータベースを設計しました。

Project social_blogging_site {
    database_type: 'PostgreSQL'
}

Table user {
    id uuid [pk]
    name varchar [not null]
    create_time timestamp [not null]
    update_time timestamp [not null]
    archive_time timestamp
}

これをdatabase.dbmlとして保存し、以下のコマンドを実行します。

dbdocs build database.dbml --project=test

無事作成が完了すると、以下のような画面が表示されます。

Visitに書かれているURLに遷移すると、プロジェクトページに遷移することができます。

また、画面上部のRelationをクリックすると各テーブルの依存関係を見ることができます。

これだけだと分かりづらいので、テーブルを増やしてみます。

Table weapon {
    id uuid [pk]
    name varchar [not null]
}

Table user_weapon {
    user_id uuid [ref: < user.id]
    weapon_id uuid [ref: < weapon.id]
}

武器を追加しました。

これをビルドコマンドで再度デプロイします。

dbdocs build database.dbml --project=test

再度Relationshipsを確認すると、テーブルが追加されていることが確認できました。

また、このポジションに関してはちゃんと管理がされているらしく、テーブルの削除や追加があっても位置を記憶してくれるみたいです。

What happens with the saved positions when a new build arrives?

When you update your project,

  • We will keep all positions of the current tables as long as they still exist in the DBML code.
  • For the new tables, there is nothing happen. So you have to arrange them on the project page.

和訳: 新しいビルドが到着すると、保存された位置はどうなりますか? プロジェクトを更新すると、

  • DBML コード内に存在する限り、現在のテーブルのすべての位置を保持します。
  • 新しいテーブルの場合は何も起こりません。 したがって、プロジェクトページ上でそれらを配置する必要があります。

パスワードを付与する

2023-07-29現在の時点では、特定の人のみに共有できるプライベートモードはないですが、パスワードによる保護が可能なようです。

なので、公開したくない場合はパスワードをチーム内で管理するのが現状のベストプラクティスのようです。

Is my dbdocs project public? How can I make it private?

Your dbdocs project is set to be public by default. However, you can follow our instructions here to protect it with a password or remove the project after testing. 和訳: 私の dbdocs プロジェクトは公開されていますか? どうすれば非公開にできますか?

dbdocs プロジェクトはデフォルトでパブリックに設定されています。 ただし、ここでの指示に従ってパスワードで保護したり、テスト後にプロジェクトを削除したりすることができます。

dbdocs.io - Database Documentation and Catalog Tool

パスワードの付与はコマンドから実行します。

dbdocs password -p test -s '[パスワードを入力]'

大文字小文字数字記号を含んだ100文字くらいのパスワードを入れてみましたが、余裕で設定できました。

SQLを出力する

さて、一番の目玉と言っても過言ではないSQL出力ですが、これはdbdocsからはできないのでコーディングする必要があります。

JSモジュールを使用してSQLを出力します。

以下のような実装を行いました。

import { exporter } from "@dbml/core";
import fs from "fs";

// get DBML file content
const dbml = fs.readFileSync('./database.dbml', 'utf-8');

const sql = exporter.export(dbml, 'postgres');

console.log(sql);

ts-nodeで実行すると、以下のようなSQLが出力されます。

CREATE TABLE "user" (
  "id" uuid PRIMARY KEY,
  "name" varchar NOT NULL,
  "create_time" timestamp NOT NULL,
  "update_time" timestamp NOT NULL,
  "archive_time" timestamp
);

CREATE TABLE "weapon" (
  "id" uuid PRIMARY KEY,
  "name" varchar NOT NULL
);

CREATE TABLE "user_weapon" (
  "user_id" uuid,
  "weapon_id" uuid
);

ALTER TABLE "user" ADD FOREIGN KEY ("id") REFERENCES "user_weapon" ("user_id");

ALTER TABLE "weapon" ADD FOREIGN KEY ("id") REFERENCES "user_weapon" ("weapon_id");

ちゃんと出力してくれていますね!

CI/CDを組んでみる

ではこれまでの知識を使ってCI/CDを組んでみましょう。

Github Actionsを使います。

(以下の記事を参考にしました。)

Creating workflow templates for your organization - GitHub Docs

まずはGithub Actionsで使用するdbdocsのトークンを発行します。

dbdocs token -g

次に、発行したトークンをリポジトリのシークレットとして設定します。

登録にはGithub CLIを使用すると便利です。

gh secret set DBDOCS_TOKEN --body "さっき発行したトークン"

ここまでできたらワークフローを実装します。

今回はビルドとSQLのエクスポートを行うところまで実装してみます。

JavaScriptを実行する部分はGithub Scriptを使用しました。

github-script/README.md at main · actions/github-script · GitHub

name: Build & Export DBDocs

on:
  push:
    branches:
      - main

jobs:
  build_and_export:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      # dbdocs CLIをインストール
      - name: Install dbdocs
        run: npm install -g dbdocs
      # dbdocs CLIを使ってDBMLをビルド&デプロイ
      - name: Build dbdocs
        run: dbdocs build database.dbml --project=test
        env:
          DBDOCS_TOKEN: ${{ secrets.DBDOCS_TOKEN }}

      # node.jsの準備
      - uses: actions/setup-node@v3
        with:
          node-version: 16
      # DBMLをSQLに変換するためのライブラリをインストール
      - run: npm install @dbml/core
      # DBMLをSQLに変換
      - name: Export dbdocs
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const { exporter } = require('@dbml/core');

            // get DBML file content
            const dbml = fs.readFileSync('./database.dbml', 'utf-8');

            // generate Postgres from DBML
            const sql = exporter.export(dbml, 'postgres');

            fs.writeFileSync('./database.sql', sql);            
      # SQLファイルをアーカイブ
      - name: Archive SQL file
        uses: actions/upload-artifact@v3
        with:
          name: database
          path: database.sql

実行に成功すると、SQLファイルがアーカイブされます。

おわり

他にも工夫すればもっと面白いことができるようになると考えています。

いま考えているのは、PrismaとDBDocsを組み合わせたDBマイグレーション基盤を考えています。

これも後でまた記事にすると思うので、良ければぜひ見てください。