これは「Vapor Advent Calendar 2018」8日目の投稿です。
はじめに
ご存知の通り、Fluent にはデータベーススキーマのマイグレーション機能があります。例えば title
などをプロパティに持つ Article
というモデルがあったとして、
...
final class Article: MySQLModel {
var id: Int?
static let entity = "article" // テーブル名
var article_id: Int?
var pub_date: Date?
var title: String?
var article_url: String?
var site_id: Int?
}
extension Article: Migration {}
...
のようにモデルを Migration
プロトコルに適合させておいて、
...
var migrations = MigrationConfig()
migrations.add(model: Article.self, database: .mysql)
services.register(migrations)
...
と設定しておくと勝手にモデルのプロパティに合わせて自動でスキーマを決定しマイグレーションしてくれるというやつですね。
※ちなみにサンプルコードには VaporとMySQLを接続する。その2 のものを参考に使わせていただきました。ありがとうございます!
この自動マイグレーション機能はとてもお手軽です。なので、世の中の Vapor 入門記事や入門書でもこのマイグレーションの実装が書かれていることがほとんどで、Vapor の Fluent でのマイグレーションはこの形だと思っている方も多いかもしれません。
それはもちろん間違いではありません。しかし、実際に本番運用させるアプリケーションでは自動マイグレーションは基本的に利用しない方がよいです。
理由は主に2つあると考えています。
- データベースの変更結果の予測が難しいこと
- テーブルの変更ができないこと
データベースの変更結果の予測が難しいこと
マイグレーションの仕組みを使うときには、データベースへの変更をトラッキングして明示的に管理したいという気持ちがあると思います。
冪等性というと語弊がありそうですが、ある特定のマイグレーションを実行したときに何が起こるかはある程度は定まったものになっていて欲しいですよね。
しかし、モデルの自動マイグレーションはモデルのプロパティの設計に依存し、その内容もプロダクトコード上に明示されるものではないため、実行するタイミングによってどのような結果になるかが予測できません。
これは本番運用していく上では正直微妙です。
テーブルの変更ができないこと
モデルの自動マイグレーションはテーブルの変更はしてくれません。つまりモデルのプロパティを変更しても ALTER TABLE
されないということです。
これ、地味にハマる人がいると思うのですが (自分も最初あれ?と思った) 、モデルを更新して何十回 Vapor を起動してもテーブルの変更をするマイグレーションは行われません。
自動マイグレーションの実装はごくナイーブなものになっていて、単純にテーブルを CREATE
/ DROP
するだけなんですね。そして、一度実行されたマイグレーションは (revert されない限りは) 2度と実行されません。
※fluent
テーブルの中を見るとマイグレーションの履歴があり、そこにクラス名が入っているのがわかると思います。マイグレーションが実行されたかどうかはそのキーに依存しています。
じゃあどうすればいいの?
Vapor におけるマイグレーションのベストプラクティスというのが明確に示されているわけではありません。
が、Fluent Migrations のドキュメントには、モデルの自動マイグレーションに言及した部分で以下のように書かれています。
This method is especially useful for quick prototyping and simple setups. For most other situations you should consider creating a normal, custom migration.
自動マイグレーションはプロトタイピングや単純な構成には向いているが、それ以外のケースでは「通常のカスタムマイグレーション」を使うべきだ、とあります。
ということで、次回は自動マイグレーションではなくカスタムマイグレーションを実装する方法を紹介したいと思います。