Component layouts
The project layout and the source set layout — how Arciphant maps components to Gradle.
Arciphant supports two ways of mapping components to Gradle:
Project layout
Creates nested Gradle projects for modules and their components — every component is a Gradle project of its own.
DefaultSource set layout
Creates one Gradle project per module and maps every component to source sets inside that project. Enabled with
sourceSetComponentLayout().
Choosing a layout
Both layouts enforce the same component structure and dependencies — they differ in how the structure is mapped to Gradle:
| Project component layout | Source set component layout | |
|---|---|---|
| Gradle projects | one per component | one per functional module |
| Gradle plugins per component | yes — registered in the DSL, see Using Plugins | no — plugins are applied per module project |
| Artifacts | one jar per component, with qualified names | no jars — consumers use the compiled component classes |
| Tests and test fixtures | standard Gradle test source set; test fixtures via the optional java-test-fixtures plugin |
Arciphant creates test and test-fixtures source sets per component |
| Configuration cache | supported | supported |
| Isolated projects (incubating) | not (yet) supported | supported |
Project layout
This is the default layout, so no configuration is needed. To select it explicitly, use projectSetComponentLayout():
arciphant {
projectSetComponentLayout()
val template = template()
.createComponent(name = "domain")
.createComponent(name = "application", apiDependencies = setOf("domain"))
module(name = "orders", template = template)
}
This creates the Gradle project :orders with subprojects :orders:domain and :orders:application:
orders
domain
src
main
test
testFixtures
application
src
main
test
testFixtures
src/testFixtures is only present once the optional java-test-fixtures plugin is applied — see
Structure test code.
No Arciphant-specific constructs are involved: modules and components are plain Gradle projects with the standard
source sets, and component dependencies are ordinary implementation and api project dependencies.
Source set layout
Enable the source set layout with sourceSetComponentLayout():
arciphant {
sourceSetComponentLayout()
val template = template()
.createComponent(name = "domain")
.createComponent(name = "application", apiDependencies = setOf("domain"))
module(name = "orders", template = template)
}
This creates the Gradle project :orders with the following source sets by default:
orders
src
domain
domainTest
domainTestFixtures
application
applicationTest
applicationTestFixtures
Other projects consume a component through the compiled classes of its source set, not through a jar — the module’s
jar stays empty. Applications packaged with Spring Boot’s bootJar still contain all component classes.
How test and test fixtures source sets are wired
A component’s test fixtures depend on its production source set; its tests depend on its test fixtures. Component dependencies are mirrored between the corresponding test-fixtures source sets, while test source sets never depend on other components’ test source sets.
The source set layout does not require the java-test-fixtures plugin. Arciphant also registers one Test task per
component test source set, named exactly like that source set, and attaches it to the module’s standard test task.
If the idea plugin is applied, test and test-fixtures directories are marked as test sources in IntelliJ IDEA.
Test source set configuration
Tests and test fixtures are enabled by default. Their names and global availability can be configured with source-set-specific settings:
arciphant {
sourceSetComponentLayout()
withTestSourceSet(true)
withTestFixturesSourceSet(false)
testSourceSetName { componentName -> "${componentName}Spec" }
testFixturesSourceSetName { componentName -> "${componentName}Fixtures" }
module(name = "orders")
.createComponent(
name = "domain",
withTestSourceSet = false,
withTestFixturesSourceSet = true,
)
}
The component parameters override the global flags.
Source-set settings and component source-set options are rejected in the project layout rather than being silently ignored.
Customizing source directories
The component source sets can be customized per module project — in its build.gradle.kts or in a
convention plugin. Arciphant registers two extensions in every functional moduleFunctional moduleDomain and library modules — modules that consist of components. Bundle modules are not functional modules.Concepts project to do so:
| Extension | Description |
|---|---|
customizeAllComponents |
Customize source sets of all components |
customizeComponent |
Customize source sets of one particular component |
Both extensions provide the following methods, each taking a lambda that customizes the respective source set:
| Method | Description |
|---|---|
productionSourceSet |
Customize the production source set |
testSourceSet |
Customize the test source set |
testFixturesSourceSet |
Customize the test fixtures source set |
In customizeAllComponents, the lambda receives the SourceSet and the component name
({ sourceSet, componentName -> … }); in customizeComponent, it receives only the SourceSet
({ sourceSet -> … }).
Example:
// all components of the module
customizeAllComponents {
productionSourceSet { sourceSet, componentName ->
sourceSet.java.setSrcDirs(listOf("$componentName/java"))
sourceSet.resources.setSrcDirs(listOf("$componentName/resources"))
}
testSourceSet { sourceSet, componentName ->
sourceSet.java.setSrcDirs(listOf("$componentName/test/java"))
}
testFixturesSourceSet { sourceSet, componentName ->
sourceSet.java.setSrcDirs(listOf("$componentName/testFixtures/java"))
}
}
// a single component, selected by name
customizeComponent("domain") {
productionSourceSet { sourceSet ->
sourceSet.java.setSrcDirs(listOf("domain/java"))
}
}
Use componentName to derive directories: for test and test-fixtures source sets it differs
from the source set name (component domain, source set domainTest). Inside customizeComponent, the
component name is available as the componentName property.
The example examples/source-set-customization
in the Arciphant repository shows a complete minimal setup: one module with three components, using
customizeAllComponents for all of them and customizeComponent for one.
Shared dependency configurations
External dependencies can be declared once for all component source sets of the same kind. In every Arciphant module project, each component configuration extends the corresponding standard configuration:
implementation,compileOnlyandruntimeOnlyreach all production source sets.testImplementation,testCompileOnlyandtestRuntimeOnlyreach all test source sets.testFixturesImplementation,testFixturesCompileOnlyandtestFixturesRuntimeOnly(created by Arciphant) reach all test fixtures source sets.- With the
java-libraryplugin applied,apireaches all production source sets andtestFixturesApi(created by Arciphant) all test fixtures source sets.
dependencies {
implementation("org.springframework:spring-context")
"testFixturesApi"("org.assertj:assertj-core:3.27.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
This also works from a convention plugin, which replaces per-component convention plugins in this layout. Prefer
applying such a convention plugin in the build.gradle.kts of each module project.
Restrictions
- Component plugins cannot be configured in the source set layout because Gradle plugins can only be applied to projects. Configure the JVM and convention plugins on the module projects instead.
disableQualifiedArchiveBaseName()is also invalid in the source set layout: components produce no archives of their own, so component-qualified archive names do not apply.
Component dependencies in build scripts
A component of another module can be referenced by the module and component names of the Arciphant configuration,
directly in the dependencies block — in the same style as external dependencies. The component extension registered
by Arciphant creates the dependency notation; it is available in both component layouts. Arciphant resolves the target
Gradle project path from the module configuration, so no project path has to be spelled out, and completes the
dependency automatically:
dependencies {
"implementation"(component(module = "contracts", component = "api"))
}The notation is an ordinary project dependency, so compile and runtime classpath resolve through Gradle’s variant
selection. Once the java-test-fixtures plugin is applied, the matching test-fixtures dependency is added
automatically.
dependencies {
"applicationImplementation"(component(module = "contracts", component = "api"))
}The notation is a dependency on the target component’s …ApiElements configuration. Arciphant adds the matching
runtime dependency to the source set’s runtimeOnly configuration and, if both the source and the target component
have a test-fixtures source set, the dependency between the test-fixtures source sets as well.
Demo projects
The Arciphant repository contains
the same demo application implemented in both layouts, so you can compare them side by side:
arciphant-project-demo uses the project layout, arciphant-source-set-demo the source set layout — see
Demo projects.