---
title: Component layouts
description: The project layout and the source set layout — how Arciphant maps components to Gradle.
icon: layout-grid
---

Arciphant supports two ways of mapping components to Gradle:

**[Project layout](#project-layout)**

Creates nested Gradle projects for modules and their components — every component is a Gradle project of its own.

<Badge variant="accent" class="mt-3">Default</Badge>

**[Source set layout](#source-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](/using-plugins)                                | no — plugins are applied per module project                        |
| Artifacts                      | one jar per component, with [qualified names](/additional-settings#qualified-archive-base-name) | 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                                                          |

:::tip[Which one?]
Choose the **project layout** if your components need their own convention plugins or artifacts — each component being
a full Gradle project gives you the complete Gradle feature set per component. Choose the **source set layout** if you
prefer fewer Gradle projects and a less nested folder structure inside your modules.
:::

## Project layout

This is the default layout, so no configuration is needed. To select it explicitly, use `projectSetComponentLayout()`:

```kotlin settings.gradle.kts {2}
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`:

<Tree>
  <Tree.Folder name="orders" icon="package" defaultOpen>
    <Tree.Folder name="domain" icon="package" defaultOpen>
      <Tree.Folder name="src" defaultOpen>
        <Tree.Folder name="main" icon="folder-code"/>
        <Tree.Folder name="test" icon="folder-code"/>
        <Tree.Folder name="testFixtures" icon="folder-code"/>
      </Tree.Folder>
    </Tree.Folder>
    <Tree.Folder name="application" icon="package" defaultOpen>
      <Tree.Folder name="src" defaultOpen>
        <Tree.Folder name="main" icon="folder-code"/>
        <Tree.Folder name="test" icon="folder-code"/>
        <Tree.Folder name="testFixtures" icon="folder-code"/>
      </Tree.Folder>
    </Tree.Folder>
  </Tree.Folder>
</Tree>

`src/testFixtures` is only present once the optional `java-test-fixtures` plugin is applied — see
[Structure test code](/declare-structure#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()`:

```kotlin settings.gradle.kts {2}
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:

<Tree>
  <Tree.Folder name="orders" icon="package" defaultOpen>
    <Tree.Folder name="src" defaultOpen>
      <Tree.Folder name="domain" icon="folder-code" />
      <Tree.Folder name="domainTest" icon="folder-code" />
      <Tree.Folder name="domainTestFixtures" icon="folder-code" />
      <Tree.Folder name="application" icon="folder-code" />
      <Tree.Folder name="applicationTest" icon="folder-code" />
      <Tree.Folder name="applicationTestFixtures" icon="folder-code" />
    </Tree.Folder>
  </Tree.Folder>
</Tree>

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.

<Expandable title="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.

</Expandable>

:::note[Component naming]
In the source set layout, component names become source set names and prefixes of the derived configurations (e.g.
`domainImplementation`). Therefore, prefer lowerCamelCase component names in this layout — e.g. `webApi` instead of
`web-api`.
:::

### 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:

```kotlin settings.gradle.kts
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 <Tooltip headline="Functional module" tip="Domain and library modules — modules that consist of components. Bundle modules are not functional modules." cta="Concepts" href="/concepts#module">functional module</Tooltip> 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:

```kotlin title="build.gradle.kts or a precompiled script plugin"
// 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.

:::note[Missing source sets and unknown components]
Components without a test or test-fixtures source set are skipped by `customizeAllComponents`; requesting
such a source set in `customizeComponent` is an error, as is an unknown component name.
:::

The example <a href="https://github.com/ergon/arciphant/tree/main/examples/source-set-customization" target="_blank" rel="noreferrer">examples/source-set-customization</a>
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`, `compileOnly` and `runtimeOnly` reach **all production** source sets.
- `testImplementation`, `testCompileOnly` and `testRuntimeOnly` reach **all test** source sets.
- `testFixturesImplementation`, `testFixturesCompileOnly` and `testFixturesRuntimeOnly` (created by Arciphant) reach
  **all test fixtures** source sets.
- With the `java-library` plugin applied, `api` reaches all production source sets and `testFixturesApi` (created by
  Arciphant) all test fixtures source sets.

```kotlin build.gradle.kts
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.

:::note
The standard `main` and `test` configurations (e.g. `implementation`, `testImplementation`) are reused for shared
dependencies, so a dependency declared there applies to all components — it cannot be limited to the `main` or `test`
source set of the module project. Do not put code into these two source sets; use the component source sets instead.
:::

### 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:

**Project layout**

```kotlin title="build.gradle.kts of the component project"
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.

**Source set layout**

```kotlin title="build.gradle.kts of the module project"
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 <a href="https://github.com/ergon/arciphant" target="_blank" rel="noreferrer">Arciphant repository</a> 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](/demo-project).
