Migrações de banco de dados em Kotlin: como gerenciar o esquema sem dor
Ao desenvolver em Kotlin, especialmente em conjunto com a JVM, mais cedo ou mais tarde surge a tarefa de sincronizar a estrutura do banco de dados entre desenvolvedores, servidores de teste e produção. A execução manual de scripts SQL leva à dessincronização e erros. A solução são as migrações de banco de dados. É uma forma automatizada de versionar alterações no esquema: criação de tabelas, adição de colunas, índices e triggers.
Neste artigo, analisaremos três ferramentas populares para Kotlin: Flyway, Liquibase e o mecanismo embutido do Exposed. Você aprenderá como integrá-las ao projeto, escrever migrações e evitar problemas típicos.
Flyway: simplicidade e versionamento
Flyway é a ferramenta mais popular para migrações no ecossistema JVM. Ela suporta Kotlin, Java e Groovy. O princípio de funcionamento: você escreve scripts SQL (ou classes Java/Kotlin), e o Flyway rastreia quais já foram aplicados, usando a tabela flyway_schema_history.
Adicionando no Gradle (Kotlin DSL):
dependencies { implementation("org.flywaydb:flyway-core:9.22.3") // para PostgreSQL implementation("org.flywaydb:flyway-database-postgresql:9.22.3")}Exemplo de migração V1__create_users.sql:
CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT NOW());
CREATE INDEX idx_users_email ON users(email);Execução a partir do código Kotlin:
import org.flywaydb.core.Flyway
fun main() { val flyway = Flyway.configure() .dataSource("jdbc:postgresql://localhost:5432/mydb", "user", "pass") .load() flyway.migrate()}Vantagens:
- Configuração mínima
- Suporte a rollback (undo) na versão paga
- Funciona com qualquer SQL
Desvantagens:
- Não possui validação de esquema embutida
- Difícil gerenciar ramificações
Liquibase: flexibilidade e declaratividade
Liquibase oferece duas abordagens: as alterações podem ser descritas em XML, YAML, JSON ou diretamente em SQL. Para projetos Kotlin, o formato YAML é conveniente — é legível e facilmente versionável.
Adicionando no Gradle:
dependencies { implementation("org.liquibase:liquibase-core:4.24.0")}Exemplo de arquivo changelog db.changelog-master.yaml:
databaseChangeLog: - changeSet: id: 1 author: developer changes: - createTable: tableName: users columns: - column: name: id type: BIGINT autoIncrement: true constraints: primaryKey: true - column: name: username type: VARCHAR(50) constraints: nullable: false unique: true - column: name: email type: VARCHAR(100) - createIndex: indexName: idx_users_email tableName: users columns: - column: name: emailExecução a partir do Kotlin:
import liquibase.Liquibaseimport liquibase.database.jvm.JdbcConnectionimport liquibase.resource.ClassLoaderResourceAccessorimport java.sql.DriverManager
fun runMigrations() { val connection = DriverManager.getConnection( "jdbc:postgresql://localhost:5432/mydb", "user", "pass" ) val database = DatabaseFactory.getInstance() .findCorrectDatabaseImplementation(JdbcConnection(connection)) val liquibase = Liquibase("db.changelog-mas