172 lines
10 KiB
Groovy
172 lines
10 KiB
Groovy
plugins {
|
|
id 'ca.spring-library'
|
|
id 'ca.spring-config'
|
|
id 'java-test-fixtures'
|
|
id 'ca.jpa-evidence'
|
|
id 'ca.jpa-test-lanes'
|
|
id 'ca.auxiliary-source-set'
|
|
}
|
|
|
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
|
// JPA persistence adapter — merged RDBMS base + PostgreSQL vendor module.
|
|
// Owns JPA entities, Spring Data repositories, mappers, transaction/audit/lock/outbox port
|
|
// implementations, and the vendor-neutral SPI interfaces (OutboxClaimRepository /
|
|
// SqlStateErrorMapping). The PostgreSQL driver, flyway-database-postgresql dialect, and vendor
|
|
// Flyway migrations live only under the .postgresql subpackage (ArchUnit keeps the base neutral).
|
|
// The JPA relational persistence platform (docs/superpowers/specs/2026-08-11-jpa-persistence-
|
|
// platform-design.md) models itself as 18 Stable library modules. This repository's fail-closed
|
|
// module registry outranks that layout, so those modules are packages here and
|
|
// JpaModuleBoundaryTest enforces the design's module dependency table. The full mapping is in
|
|
// docs/jpa/repository-adaptation.md.
|
|
//
|
|
// The testkit is its own source set rather than part of `test` because more than one lane consumes
|
|
// it and because a source set whose dependencies are declared only on the test configurations gives
|
|
// the design's "no production module depends on the testkit" guarantee without a new Gradle project.
|
|
auxiliarySourceSets {
|
|
sourceSet('postgresqlIntegrationTest') {
|
|
// 'testFixtures' as well as 'main': the integration lane consumed the testkit through the
|
|
// convention's `consumedBy 'test', 'postgresqlIntegrationTest'`, and java-test-fixtures only
|
|
// wires `test` on its own. The lane has to say so (ADR-BUILD-001).
|
|
compilesAgainst 'main', 'testFixtures'
|
|
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
|
}
|
|
sourceSet('jpaPlatformPerformanceTest') { compilesAgainst 'main', 'testFixtures' }
|
|
}
|
|
|
|
dependencies {
|
|
implementation project(':application-core')
|
|
implementation project(':shared-contract')
|
|
|
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
|
// feature-distributed-lock-contract: Spring Integration JDBC LockRegistry backs the
|
|
// multi-instance distributedLockProvider. Version managed by Spring Boot BOM.
|
|
implementation 'org.springframework.integration:spring-integration-jdbc'
|
|
|
|
// Vendor (PostgreSQL): Flyway migration API + PostgreSQL driver/dialect. Used only by the
|
|
// .postgresql subpackage; the RDBMS base stays vendor-neutral (PERSISTENCE_RDBMS_STAYS_VENDOR_NEUTRAL).
|
|
implementation 'org.springframework.boot:spring-boot-starter-flyway'
|
|
runtimeOnly 'org.postgresql:postgresql'
|
|
runtimeOnly 'org.flywaydb:flyway-database-postgresql'
|
|
|
|
// Vendor (H2): the local-profile driver. Used only by the .h2 subpackage, which reaches it
|
|
// through JDBC/JPA rather than by importing org.h2 types — the same shape as the PostgreSQL
|
|
// driver above. Not `developmentOnly`: local is a deployable profile of this artifact, and the
|
|
// vendor selector, not the packaging, decides which driver a deployment loads.
|
|
runtimeOnly 'com.h2database:h2'
|
|
|
|
// JPA platform observability (design §37). Micrometer's observation API already arrives with
|
|
// Spring; the meter registry does not, and the platform's transaction/query/retry metrics need
|
|
// it. Version managed by the Spring Boot BOM.
|
|
implementation 'io.micrometer:micrometer-core'
|
|
|
|
// Querydsl and Envers are Advanced opt-ins (design §4.2): the platform implements their
|
|
// contracts, but the Stable runtime classpath must not carry either. compileOnly keeps them off
|
|
// every deployment while still compiling the support classes; a deployment that opts in adds the
|
|
// artifact itself, and the guards refuse the capability when the classes are absent.
|
|
compileOnly 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
|
|
compileOnly 'org.hibernate.orm:hibernate-envers'
|
|
|
|
testImplementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
|
|
testImplementation 'org.hibernate.orm:hibernate-envers'
|
|
testImplementation libs.archunit.junit5
|
|
|
|
postgresqlIntegrationTestImplementation 'org.testcontainers:testcontainers-postgresql'
|
|
postgresqlIntegrationTestImplementation 'org.testcontainers:testcontainers-junit-jupiter'
|
|
postgresqlIntegrationTestImplementation 'org.testcontainers:testcontainers-toxiproxy'
|
|
postgresqlIntegrationTestRuntimeOnly 'org.postgresql:postgresql'
|
|
|
|
// Declared rather than inherited: the `testkit` source set extended testImplementation, so the
|
|
// fixtures compiled against every test library this leaf declared without stating any of them
|
|
// (ADR-BUILD-001). Listing them makes the fixtures' own surface reviewable.
|
|
testFixturesImplementation 'jakarta.persistence:jakarta.persistence-api'
|
|
testFixturesImplementation 'org.hibernate.orm:hibernate-envers'
|
|
testFixturesImplementation 'org.flywaydb:flyway-core'
|
|
testFixturesImplementation 'org.springframework:spring-jdbc'
|
|
// The rule pack names Spring Data's Repository marker in a rule; it needs the type on the
|
|
// compile classpath even though it never calls it.
|
|
testFixturesImplementation 'org.springframework.data:spring-data-commons'
|
|
testFixturesImplementation 'org.junit.jupiter:junit-jupiter-api'
|
|
testFixturesImplementation libs.toxiproxy.java
|
|
testFixturesImplementation 'org.testcontainers:testcontainers'
|
|
testFixturesImplementation 'org.testcontainers:testcontainers-postgresql'
|
|
testFixturesImplementation 'org.testcontainers:testcontainers-junit-jupiter'
|
|
testFixturesImplementation 'org.testcontainers:testcontainers-toxiproxy'
|
|
testFixturesImplementation libs.archunit.junit5
|
|
testFixturesRuntimeOnly 'org.postgresql:postgresql'
|
|
|
|
// The performance lane starts its own servers: pool saturation is only observable against a
|
|
// real database, because the thing being measured is what happens when every connection to it
|
|
// is already held.
|
|
jpaPlatformPerformanceTestImplementation 'org.testcontainers:testcontainers'
|
|
jpaPlatformPerformanceTestImplementation 'org.testcontainers:testcontainers-postgresql'
|
|
jpaPlatformPerformanceTestImplementation 'org.testcontainers:testcontainers-junit-jupiter'
|
|
jpaPlatformPerformanceTestRuntimeOnly 'org.postgresql:postgresql'
|
|
}
|
|
|
|
// PostgreSQL readiness, tagged platform, and pool-contract lanes are owned by
|
|
// `ca.jpa-test-lanes`. Their metadata is represented as Java records, so task names, selectors,
|
|
// tags, descriptions and runtime property wiring are compiled rather than assembled from Groovy Maps.
|
|
// The security lane includes the runtime-role namespace-denial method selector in that typed model.
|
|
|
|
tasks.named('postgresqlSecurityBaselineIntegrationTest') {
|
|
// Cross-leaf task edge: see the handoff. verifyCleanArchitectureDependencies inspects
|
|
// configurations, not the task graph, so this edge is invisible to it.
|
|
dependsOn project(':adapter:inbound:web').tasks.named('jpaPersistenceRedactionContractTest')
|
|
}
|
|
|
|
// Which JPA lanes block a release is declared by .github/workflows/jpa-release.yml.
|
|
// This leaf declares the lanes and how each runs; it does not own release orchestration.
|
|
|
|
// The unit lane reads three files that are not Java sources: the release registry and its two
|
|
// renderings. Without declaring them, Gradle calls the lane up-to-date after a registry demotion or
|
|
// a workflow edit — so the drift check that exists to catch exactly that edit never runs on it.
|
|
// The unit lane reads files that are not Java sources: the release registry, its two renderings,
|
|
// and the documents that describe the pool lane. Without declaring them, Gradle calls the lane
|
|
// up-to-date after a registry demotion or a workflow edit — so the drift checks that exist to catch
|
|
// exactly those edits never run on them.
|
|
tasks.named('test') {
|
|
inputs.file(rootProject.file('config/jpa/release-registry.json'))
|
|
inputs.file(new File(rootProject.projectDir.parentFile, 'docs/jpa/support-matrix.md'))
|
|
inputs.file(new File(rootProject.projectDir.parentFile, 'docs/jpa/repository-adaptation.md'))
|
|
// The whole directory, not the two named workflows: the Experimental-major check asks whether
|
|
// *some* lane records that major as its target, so adding or deleting any workflow can change
|
|
// its answer. Naming files here would leave the lane that matters outside the up-to-date check.
|
|
inputs.dir(new File(rootProject.projectDir.parentFile, '.github/workflows'))
|
|
inputs.file(file('build.gradle'))
|
|
inputs.dir(file('src/jpaPlatformPerformanceTest/java'))
|
|
}
|
|
|
|
// verifyJpaApiSurface — every public type this leaf exposes is a committed decision.
|
|
//
|
|
// The GraphQL and Mongo leaves already carried this; the largest of the three did not, so the one
|
|
// public surface with the most adopters was the one nothing had an opinion about. The convention
|
|
// plugin is opt-in per leaf, and opting in was simply never done here.
|
|
//
|
|
// This is a record, not a budget, and the distinction matters. A snapshot shrinks nothing on its
|
|
// own: the GraphQL surface grew from 373 types to 398 while under one, each addition approved and
|
|
// none refused. What the baseline buys is that growth is visible in review at the moment it
|
|
// happens and that the number is available to argue with — not that the number cannot rise. A
|
|
// ceiling the approval flag cannot lift is a separate decision nobody has taken yet.
|
|
// `api` is the surface an adopter is meant to reach; everything else here is a candidate to become
|
|
// internal at that point.
|
|
apiSurface {
|
|
label = 'Jpa'
|
|
baseline = rootProject.file('../docs/architecture/jpa-api-surface.txt')
|
|
description = 'JPA persistence leaf public API surface — every public top-level type in src/main/java.'
|
|
rationale = [
|
|
'A public type in a single-jar leaf is reachable from every adopter\'s code, so',
|
|
'additions are reviewed rather than discovered. `api` is the intended external',
|
|
'surface; the rest is implementation that has not been moved under an internal',
|
|
'root yet.',
|
|
]
|
|
}
|
|
|
|
|
|
|
|
// The JPA readiness registry describes this platform's lanes and resolves their task paths, so it
|
|
// runs with this leaf's `check` rather than with all 62. The task itself is registered by
|
|
// the ca.jpa-qualification plugin from build-tools, which the root applies.
|
|
tasks.named('check') {
|
|
dependsOn rootProject.tasks.named('verifyJpaReadinessRegistry')
|
|
}
|