In Spring it s possible to define bean dependencies in separate modules, which are then resolved via the classpath
at runtime. Is it possible to do something similar in Quarkus?
For example, a multi-module setup that looks like this:
- service
- service-test
- service-artifact
In Spring it s possible to define @Configuration
in the service
module, that resolves concrete dependencies at runtime via the classpath
of its current context, either service-test
or service-artifact
, allowing injection of dummy or test dependencies when under test, and real ones in the production artifact.
For example, a class in service
requires an instance of SomeInterface
. The implementation of SomeInterface
is defined in either the -test
or -artifact
module. The service
module has no direct dependency on either the -test
or -artifact
modules.
Some code:
In the service
module:
@ApplicationScoped
class OrderService(private val repository: OrderRepository) {
fun process(order: Order) {
repository.save(order)
}
}
interface OrderRepository {
fun save(order: Order)
}
In the service-test
module:
class InMemoryOrderRepository : OrderRepository {
val orders = mutableListOf<Order>()
override fun save(order: Order) {
orders.add(order)
}
}
class OrderServiceTestConfig {
@ApplicationScoped
fun orderRepository(): OrderRepository {
return InMemoryOrderRepository()
}
}
@QuarkusTest
class OrderServiceTest {
@Inject
private lateinit var service: OrderService
@Test
fun `injected order service with resolved repository dependency`() {
// This builds and runs OK
service.process(Order("some_test_order"))
}
}
Where I have tried to replicate a Spring-style setup as above in Quarkus, ArC validation is failing with UnsatisfiedResolutionException
on the build of the service
module, even though everywhere it is actually consumed provides the correct dependencies; a test successfully resolves the dependency and passes.
How do I achieve the separation of dependency interface from the implementation, and keep ArC validation happy, with Quarkus?
(Note: this behaviour occurs with Java and Maven also.)
I have included a maven example here. Note that ./mvnw install
fails with the UnsatisfiedResolutionException
but that it s possible to build and run the test successfully using ./mvnw test
.
Build files:
root project build.gradle.kts
:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.3.72"
kotlin("plugin.allopen") version "1.3.72"
}
allprojects {
group = "my-group"
version = "1.0.0-SNAPSHOT"
repositories {
mavenLocal()
mavenCentral()
}
}
subprojects {
apply {
plugin("kotlin")
plugin("kotlin-allopen")
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
allOpen {
annotation("javax.ws.rs.Path")
annotation("javax.enterprise.context.ApplicationScoped")
annotation("io.quarkus.test.junit.QuarkusTest")
}
apply {
plugin("kotlin")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
kotlinOptions.javaParameters = true
}
}
build.gradle.kts
for service
:
import io.quarkus.gradle.tasks.QuarkusDev
plugins {
id("io.quarkus") version "1.9.1.Final"
}
apply {
plugin("io.quarkus")
}
dependencies {
implementation(project(":common:model"))
implementation(enforcedPlatform("io.quarkus:quarkus-universe-bom:1.9.1.Final"))
implementation("io.quarkus:quarkus-kotlin")
}
build.gradle.kts
for service-test
:
import io.quarkus.gradle.tasks.QuarkusDev
plugins {
id("io.quarkus") version "1.9.1.Final"
}
apply {
plugin("io.quarkus")
}
dependencies {
implementation(project(":service"))
implementation(enforcedPlatform("io.quarkus:quarkus-universe-bom:1.9.1.Final"))
implementation("io.quarkus:quarkus-kotlin")
testImplementation("io.quarkus:quarkus-junit5")
}