データベースの移行は、特に継続的インテグレーションとデリバリー (CI/CD) が標準的な実践である環境では、ソフトウェア開発の重要な側面です。アプリケーションが成長し、進化するにつれて、アプリケーションが依存するデータベース スキーマも変化する必要があります。これらのスキーマ変更を手動で管理すると、エラーが発生し、かなりの時間がかかる可能性があります。
データベースの移行を簡素化するために調整された貴重なオープンソース ツールである Flyway を紹介します。 Flyway はデータベースにバージョン管理を導入し、スキーマを安全かつ確実に移行できるようにします。この記事では、Flyway を使用してマルチモジュールの Gragle Java プロジェクトでデータベースの移行を自動化し、データベースの変更管理が合理化され、エラーが発生しにくいプロセスになるようにする方法を検討します。
フライウェイの詳細
一部の小規模なプロジェクトやモノリシック アプリケーションは、1 つのビルド ファイルと統一されたソース構造だけで管理できる場合がありますが、大規模なプロジェクトは、相互依存する複数のモジュールに編成されることがよくあります。ここでは「相互依存」という用語が重要であり、単一のビルド プロセスを介してこれらのモジュールを接続する必要性を強調しています。
Gradle は、マルチモジュール プロジェクトと呼ばれることが多いマルチプロジェクト ビルド機能を使用して、このセットアップに対応します。 Gradle の用語では、これらのモジュールはサブプロジェクトと呼ばれます。
マルチプロジェクト ビルドは 1 つのルート プロジェクトを中心に構成され、その下に複数のサブプロジェクトを含めることができます。
ディレクトリ構造は次のようになります:
├── .gradle │ └── ⋮ ├── gradle │ ├── libs.versions.toml │ └── wrapper ├── gradlew ├── gradlew.bat ├── settings.gradle.kts (1) ├── sub-project-1 │ └── build.gradle.kts (2) ├── sub-project-2 │ └── build.gradle.kts (2) └── sub-project-3 └── build.gradle.kts (2)
(1) settings.gradle.kts ファイルにはすべてのサブプロジェクトが含まれている必要があります。
(2) 各サブプロジェクトには独自の build.gradle.kts ファイルが必要です。
クリーン アーキテクチャは、関心事の分離を強調する設計パターンであり、ソフトウェアの保守とテストが容易になります。このアーキテクチャをプロジェクトに実装する実際的な方法の 1 つは、Gradle のサブモジュール構造を使用してコードベースを編成することです。 Clean Architecture と Gradle サブモジュールを調整する方法は次のとおりです:
クリーンなアーキテクチャ層:
コア:
外部:
ウェブ:
├── .gradle │ └── ⋮ ├── gradle │ ├── libs.versions.toml │ └── wrapper ├── gradlew ├── gradlew.bat ├── settings.gradle.kts (1) ├── sub-project-1 │ └── build.gradle.kts (2) ├── sub-project-2 │ └── build.gradle.kts (2) └── sub-project-3 └── build.gradle.kts (2)
ステップ 1: Java ベースの Gradle プロジェクトを作成し、「SchoolStaff」という名前を付けます。
ステップ 2: Spring Initializr に移動し、Web という名前の REST API プロジェクトを生成します。
ステップ 3: Java ベースの Gradle プロジェクトを作成し、外部 という名前を付けます。
ステップ 4: Java ベースの Gradle プロジェクトを作成し、Core という名前を付けます。
ルート build.gradle.kts
SchoolStaff/ ├── Core/ │ ├── src/ │ │ └── main/ │ │ ├── java/ # Business logic and domain objects │ │ └── resources/ # Core-specific resources (if any) │ └── build.gradle.kts ├── External/ │ ├── src/ │ │ └── main/ │ │ ├── java/ # External integration code │ │ └── resources/ # db/migration and other external resources │ └── build.gradle.kts ├── Web/ │ ├── src/ │ │ └── main/ │ │ ├── java/ # REST controllers and entry-point logic │ │ └── resources/ # Application-specific configuration │ └── build.gradle.kts ├── build.gradle.kts # Root Gradle build └── settings.gradle.kts # Project module settings
settings.gradle.kts
plugins { id("java") } allprojects { group = "school.staff" version = "1.0.0" repositories { mavenLocal() mavenCentral() } } subprojects { apply(plugin = "java") dependencies { testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") } tasks.test { useJUnitPlatform() } }
「Web」プロジェクトに必要な依存関係。
rootProject.name = "SchoolStaff" include("Core", "External", "Web")
「コア」プロジェクトに必要な依存関係。
dependencies { implementation(project(":Core")) implementation(project(":External")) }
「外部」プロジェクトに必要な依存関係。
dependencies { runtimeOnly(project(":External")) }
Flyway の移行には次のプラグインを使用します:
import java.sql.DriverManager import java.util.Properties // Function to load properties based on the environment fun loadProperties(env: String): Properties { val properties = Properties() val propsFile = file("../web/src/main/resources/application-$env.properties") if (propsFile.exists()) { propsFile.inputStream().use { properties.load(it) } } else { throw GradleException("Properties file for environment '$env' not found: ${propsFile.absolutePath}") } return properties } // Set the environment (default is 'dev' if no argument is passed) val env = project.findProperty("env")?.toString() ?: "dev" // Load properties for the chosen environment val dbProps = loadProperties(env) buildscript { dependencies { classpath("org.flywaydb:flyway-database-postgresql:11.1.0") // This is required for the flyway plugin to work on the migration, otherwise it will throw an error as No Database found classpath("org.postgresql:postgresql:42.7.4") } } plugins { id("java-library") id("org.flywaydb.flyway") version "11.0.1" } group = "school.staff" version = "unspecified" repositories { mavenLocal() mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa:3.4.0") implementation("org.postgresql:postgresql:42.7.4") implementation("org.flywaydb:flyway-core:11.0.1") implementation("org.flywaydb:flyway-database-postgresql:11.0.1") implementation("org.flywaydb:flyway-gradle-plugin:11.0.1") implementation (project(":Core")) testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") } tasks.test { useJUnitPlatform() } // Task to create the database if it doesn't exist tasks.register("createDatabase") { doLast { val dbUrl = dbProps["spring.datasource.url"] as String val dbUsername = dbProps["spring.datasource.username"] as String val dbPassword = dbProps["spring.datasource.password"] as String // Extract the base URL and database name val baseDbUrl = dbUrl.substringBeforeLast("/")+ "/" val dbName = dbUrl.substringAfterLast("/") // Connect to the PostgreSQL server (without the specific database) DriverManager.getConnection(baseDbUrl, dbUsername, dbPassword).use { connection -> val stmt = connection.createStatement() val resultSet = stmt.executeQuery("SELECT 1 FROM pg_database WHERE datname = '$dbName'") if (!resultSet.next()) { println("Database '$dbName' does not exist. Creating it...") stmt.executeUpdate("CREATE DATABASE \"$dbName\"") println("Database '$dbName' created successfully.") } else { println("Database '$dbName' already exists.") } } } } flyway { url = dbProps["spring.datasource.url"] as String user = dbProps["spring.datasource.username"] as String password = dbProps["spring.datasource.password"] as String locations = arrayOf("classpath:db/migration") baselineOnMigrate = true } //Ensure classes are built before migration tasks.named("flywayMigrate").configure { dependsOn(tasks.named("createDatabase")) dependsOn(tasks.named("classes")) }
このアプローチは、制御された信頼性の高い移行を保証するため、実稼働環境に最適です。アプリケーションの起動ごとに自動的に移行を実行するのではなく、必要な場合にのみ移行を実行することで、より優れた柔軟性と制御を提供します。
Spring アプリケーションの application.properties ファイルも利用して、データベース接続と資格情報を管理しています。 BaselineOnMigrate = true 設定により、最初の移行が将来の移行のベースラインとして使用されます。
plugins { id("org.flywaydb.flyway") version "11.0.1" }
JPA Buddy を使用して、外部プロジェクトの resource/db/migration ディレクトリ内にすべての移行ファイルを生成できます。
V1__Initial_Migration
flyway { url = dbProps["spring.datasource.url"] as String user = dbProps["spring.datasource.username"] as String password = dbProps["spring.datasource.password"] as String locations = arrayOf("classpath:db/migration") baselineOnMigrate = true }
ルート プロジェクトから、次のコマンドを使用して Flyway 移行を実行できます。
CREATE TABLE _user ( id UUID NOT NULL, created_by UUID, created_date TIMESTAMP WITH TIME ZONE, last_modified_by UUID, last_modified_date TIMESTAMP WITH TIME ZONE, first_name VARCHAR(255), last_name VARCHAR(255), email VARCHAR(255), password VARCHAR(255), tenant_id UUID, CONSTRAINT pk__user PRIMARY KEY (id) );
これにより、すべての移行ファイルがデータベースに適用されます。
Gradle マルチモジュール プロジェクト内で Flyway を使用してデータベース移行を自動化する方法を検討しました。これは、CI/CD 環境でスキーマの一貫性を維持するために重要です。
Gradle がマルチプロジェクト ビルドをサポートし、複雑なプロジェクトを管理しやすいサブプロジェクトに編成し、それぞれが独自のビルド構成を持ち、ルート ビルド スクリプトの下に統合される方法についても説明しました。
最後に、クリーン アーキテクチャと Gradle モジュールを調整し、プロジェクトをコア層、外部層、Web 層に構造化し、懸念事項と依存関係管理の明確な分離を促進しました。
これらの実践により、モジュール性、自動化、保守性が強化され、スケーラブルでエラーのないソフトウェア開発の準備が整えられます。
以上がマルチモジュール Gradle プロジェクトでの Flyway の移行 (クリーン アーキテクチャ)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。