契約をプロデューサーに保存する代わりに、契約で共通リポジトリを使用するにはどうすればよいですか ?
契約書をプロデューサーと一緒に保管するのではなく、契約書を共通の場所に保管するもう 1 つの方法があります。この状況は、セキュリティの課題 (コンシューマーがプロデューサーのコードを複製できない場合) に関連している可能性があります。また、契約を 1 か所にまとめておけば、プロデューサーとして、コンシューマーが何人いるのか、ローカルの変更でどのコンシューマーが破棄される可能性があるのかがわかります。
リポジトリ構造
座標 com.example:server
を持つプロデューサーと 3 つのコンシューマー client1
、client2
、client3
があると仮定します。次に、共通の契約を含むリポジトリで、次のセットアップを行うことができます (Spring Cloud Contract のリポジトリ [GitHub] (英語) samples/standalone/contracts
サブフォルダーでチェックアウトできます)。次のリストは、そのような構造を示しています。
├── com
│ └── example
│ └── server
│ ├── client1
│ │ └── expectation.groovy
│ ├── client2
│ │ └── expectation.groovy
│ ├── client3
│ │ └── expectation.groovy
│ └── pom.xml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── assembly
└── contracts.xml
スラッシュ区切りの groupid/artifact id
フォルダー (com/example/server
) には、3 つのコンシューマー (client1
、client2
、client3
) が期待されています。このドキュメント全体に従って、期待されるのは標準の Groovy DSL 契約ファイルです。このリポジトリは、リポジトリのコンテンツに 1 対 1 でマップする JAR ファイルを生成する必要があります。
次の例は、server
フォルダー内の pom.xml
ファイルを示しています。
Spring Cloud Contract Maven プラグイン以外には依存関係はありません。これらの pom.xml
ファイルは、コンシューマー側で mvn clean install -DskipTests
を実行してプロデューサープロジェクトのスタブをローカルにインストールするために必要です。
ルートフォルダー内の pom.xml
ファイルは次のようになります。
アセンブリプラグインを使用して、すべての契約を含む JAR を構築します。次の例は、そのようなセットアップを示しています。
ワークフロー
このワークフローでは、Spring Cloud Contract がコンシューマー側とプロデューサー側の両方でセットアップされていることを前提としています。契約との共通リポジトリには、適切なプラグインのセットアップもあります。CI ジョブは、すべての契約のアーティファクトを構築し、Nexus または Artifactory にアップロードするための共通リポジトリ用に設定されています。次の図は、このワークフローの UML を示しています。
コンシューマー
コンシューマーがプロデューサーコードを複製する代わりに、契約をオフラインで作業したい場合、コンシューマーチームは共通リポジトリを複製し、必要なプロデューサーのフォルダー ( com/example/server
など) に移動し、mvn clean install -DskipTests
を実行して契約から変換されたスタブをローカルにインストールします。
プロデューサー
プロデューサーは、次のように、Spring Cloud Contract Verifier を変更して、契約を含む JAR の URL と依存関係を提供できます。
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<configuration>
<contractsMode>REMOTE</contractsMode>
<contractsRepositoryUrl>
https://link/to/your/nexus/or/artifactory/or/sth
</contractsRepositoryUrl>
<contractDependency>
<groupId>com.example.standalone</groupId>
<artifactId>contracts</artifactId>
</contractDependency>
</configuration>
</plugin>
この設定では、groupid
または com.example.standalone
および artifactid
または contracts
を含む JAR が link/to/your/nexus/or/artifactory/or/sth (英語)
からダウンロードされます。次に、ローカルの一時フォルダーで解凍され、com/example/server
に存在する契約が、テストとスタブの生成に使用される契約として選択されます。この規則により、プロデューサーチームは、互換性のない変更が行われたときにどのコンシューマーチームが壊れているかを知ることができます。
残りの流れは同じように見えます。
プロデューサーごとではなくトピックごとにメッセージング契約を定義するにはどうすればよいですか ?
共通リポジトリでのメッセージング契約の重複を避けるために、少数のプロデューサーが 1 つのトピックにメッセージを書き込む場合、REST 契約がプロデューサーごとのフォルダーに配置され、メッセージング契約がトピックごとのフォルダーに配置される構造を作成できます。
Maven プロジェクトの場合
プロデューサー側で作業できるようにするには、関心のあるトピックをメッセージングすることによって共通リポジトリ jar ファイルをフィルタリングするための包含パターンを指定する必要があります。Maven Spring Cloud Contract プラグインの includedFiles
プロパティを使用すると、これが可能になります。また、デフォルトのパスは共通リポジトリ groupid/artifactid
となるため、contractsPath
を指定する必要があります。次の例は、Spring Cloud Contract 用の Maven プラグインを示しています。
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<configuration>
<contractsMode>REMOTE</contractsMode>
<contractsRepositoryUrl>https://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
<contractDependency>
<groupId>com.example</groupId>
<artifactId>common-repo-with-contracts</artifactId>
<version>+</version>
</contractDependency>
<contractsPath>/</contractsPath>
<baseClassMappings>
<baseClassMapping>
<contractPackageRegex>.*messaging.*</contractPackageRegex>
<baseClassFQN>com.example.services.MessagingBase</baseClassFQN>
</baseClassMapping>
<baseClassMapping>
<contractPackageRegex>.*rest.*</contractPackageRegex>
<baseClassFQN>com.example.services.TestBase</baseClassFQN>
</baseClassMapping>
</baseClassMappings>
<includedFiles>
<includedFile>**/${project.artifactId}/**</includedFile>
<includedFile>**/${first-topic}/**</includedFile>
<includedFile>**/${second-topic}/**</includedFile>
</includedFiles>
</configuration>
</plugin>
前述の Maven プラグインの値の多くは変更できます。「典型的な」例を提供しようとするのではなく、説明の目的でこれを含めました。 |
Gradle プロジェクトの場合
Gradle プロジェクトを操作するには:
次のように、共通リポジトリの依存関係のカスタム構成を追加します。
ext { contractsGroupId = "com.example" contractsArtifactId = "common-repo" contractsVersion = "1.2.3" } configurations { contracts { transitive = false } }
次のように、共通リポジトリの依存関係をクラスパスに追加します。
dependencies { contracts "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" testCompile "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" }
次のように、依存関係を適切なフォルダーにダウンロードします。
task getContracts(type: Copy) { from configurations.contracts into new File(project.buildDir, "downloadedContracts") }
次のように JAR を解凍します。
task unzipContracts(type: Copy) { def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar") def outputDir = file("${buildDir}/unpackedContracts") from zipTree(zipFile) into outputDir }
次のように、未使用の契約をクリーンアップします。
task deleteUnwantedContracts(type: Delete) { delete fileTree(dir: "${buildDir}/unpackedContracts", include: "**/*", excludes: [ "**/${project.name}/**"", "**/${first-topic}/**", "**/${second-topic}/**"]) }
次のようにタスクの依存関係を作成します。
unzipContracts.dependsOn("getContracts") deleteUnwantedContracts.dependsOn("unzipContracts") build.dependsOn("deleteUnwantedContracts")
次のように
contractsDslDir
プロパティを設定して、契約が含まれるディレクトリを指定してプラグインを構成します。contracts { contractsDslDir = new File("${buildDir}/unpackedContracts") }