Flywayは使い物にならない

2019年12月15日

Flywayをしばらく使ってみたのだが、わざわざこれを使う意味が無いように感じるし、かえって煩わしくさえ感じるところだ。もちろん、その理由のほとんどはFlywayのせいではなく、SQL、しかもデータベースごとに方言の大きいDDL文のせいなのだが、Flywayを使うことによってその面倒さが逆に際立つような気がする。

Flywayが使い物にならない理由

エラーの復旧は手作業

Flywayはスキーマの作成や更新のために、手書きで単純にマイグレーションファイル中にSQLを記述し、それを順番に実行するだけの仕組みだ。まず、その中身については事前にチェックされないのでエラーが発生する場合がある。

一つのFlyway用ファイルに複数のSQL文があり、その途中でエラーが発生した場合には、その復旧は手作業になってしまう。つまり、途中までは成功しているので、成功した部分を手作業でキャンセルしなければならない。

例えば、二つのテーブルA,Bをcreateするとして、テーブルAのcreateは成功したが、テーブルBのcreateがエラーになったとする。この場合、修正して再実行する前に、あらかじめテーブルAを手でdropしなければならない。Flywayはそこまで面倒見てくれないようだ。

もしかしたら有料版にはこの機能があるのかもしれないが。

SQLの分割が面倒

上記の問題を解決しようとすると、SQL一文ごとに別のマイグレーションファイルを記述することになる。これなら、あるファイルでエラーが発生しても、そのファイルを修正するだけで再実行することが可能だ。

しかしこの場合には、create table、create index、insert **、update **等のSQL文ごとにファイルを作成しなければならない。これは面倒でやっていられない。Flywayでは、あくまでも実行単位が「ファイル」なので、手作業でエラー復旧を行う面倒さを避けるためには、ファイルを分割しなければならず、ファイル数が膨大なものになりうる。

集約されたSQL文を作ってはくれない

例えば、あるテーブルAを作成し、開発が進むにつれ、その列属性を変更したり、列を追加したりしていくとする。最終的には、開発初期段階でcreate tableしたものとは全く違ったものになっている可能性がある。

しかし、Flywayは最終的に稼働しているスキーマを生成するDDL文を作成してはくれない。それまで適用されたDDL文は複数のマイグレーションファイルの中に散乱している状態のままだ。

例えば、開発が一応終了して本番時には、わかりやすい形でマイグレーションファイルを整理したいとすると、これも手作業で行わなければならない。しかし、新たに作成し直したマイグレーションファイルを開発データベースに適用することはできない。Flywayはそれまでの履歴を記録しているため、過去のマイグレーションファイルを修正してはいけないのだそうだ。

※もちろん、手作業でマイグレーションファイルを整理した場合、間違いが発生する危険性もある。

ドキュメントを作ってくれない

これは「当然だろ」と言われるかもしれないが、ドキュメントを作成してはくれない。例えば、もともとSQLには以下のようにコメントをつけられるのだが、こういったコメントをつけていてもFlywayでは無視されるだけだ。

また、前項に書いたようにSQLを集約してはくれないので、例えば「コメント付きの最終的なテーブルを表すSQL文」といったものも作成してはくれない。別途「スキーマ一覧」といった文書を作成する必要がある。

/* this is test table */
create table test (
  /* record id */
  id integer not null
)

つまり、最終的に一つのテーブルを作成するマイグレーションファイルは複数に分割されており、手で集約しなければならず、ドキュメントも手で改変していく必要がある。

SQLの方言を吸収してくれない

SQLの中でも特にDDLは、RDBシステムによる方言が大きい。作成済のスキーマに対する方言の違いは、jOOQを使えば何とかなるのだが、jOOQではDDLのサポートは貧弱だ。jOOQの仕組みからいって、そこまでのサポートができないことは容易に理解できる。

その一方でFlywayでは、方言の吸収については何も考えられておらず、単純に「対象とするRDBのDDL方言」を使わなければならない。したがって、複数のRDBに対応したい場合には、それぞれ別の方言で記述しなければならない。これはいかにも現代的では無いだろう。

理想的なマイグレーションシステム

Flywayを一通り見て、理想的なRDBマイグレーションシステムがどんなものかのイメージを考えてみた。

RDBの方言を吸収してくれること

例えば、RDBによって列タイプの記述方法が全く異なるのだが、これを「仮想的な列タイプ」として記述し、物理的なRDBごとに実際の型に変換してくれるような仕組みが望ましい。

どのようなRDBにも、ほぼ概念的に等価なものが備わっているのだから、列タイプやら列属性の書き方の違いなどでわずらわされたくない。

型安全であること

「SQLを手で記述する」と、とかく間違いが発生しがちである。なぜなら、記述の時点では構文解析などが一切されないからだ。例えば、jOOQのようにこれをJavaプログラムとして記述できるようにすれば、記述時点である程度のエラーチェックが可能になる。

したがって、このマイグレーションシステムでは、Javaオブジェクトを組み合わせることでスキーマを表現するものであり、これらのJavaオブジェクトは特定のRDBによらない概念的・仮想的な表現を行うものになる。

最終的な状態を集約できること

Javaオブジェクトによって表現されたスキーマは開発が進むにつれ、変化していくわけだが、いつでも現在のスキーマを完全に表現するオブジェクトの生成ができることが必要になる。これは、現在のスキーマを表現するJavaのソースコードになるだろう。

これは、物理的RDBのスキーマ構造をそのままJavaソースとして表したものであり、開発者側にとってはスキーマの修正が非常に楽になるはずだ。

ドキュメントを自動生成できること

テーブルや列にコメントを付加しておけば、自動的にこれをひろいあげ、HTML等の文書を作成できることが望ましい。もちろんこれらのコメントはJavadocとしてではなく、Javaオブジェクトに付加された文字列でよい。