Document generation
katachi can generate Markdown documentation from an architecture { } definition.
Basics
Section titled “Basics”Configure it in build.gradle.kts.
katachi { // ⭐️ The fully qualified name of the property holding your `architecture { }` architecture = "com.example.projectArchitecture"}Once that is set, running the katachiDocs Gradle task generates the documentation.
./gradlew katachiDocsExample output
Section titled “Example output”Take the following definition as an example.
architecture { title = "myapp"
"domain".group { title = "Domain layer" summary = "App-specific behavior and the values it acts on"
"UseCase" { title = "Use case" summary = "A single app-specific behavior that happens on a screen" example("GetItemListUseCase", "The list of items") example("SignInUseCase", "Signing in") layout { "core/domain" / mainSourceSet / kotlin / "com/example/core/domain/useCase" / "*UseCase".ktFile() } "has only an invoke method".konsist { /* ... */ } }
"RepositoryInterface" { title = "Repository" summary = "The boundary where data is fetched and saved" layout { "core/domain" / mainSourceSet / kotlin / "com/example/core/domain/repository" / "*Repository".ktFile() } } }}Directorybuild/katachi/docs/
- README.md
Directorydomain/
- README.md
- UseCase.md
- RepositoryInterface.md
# myapp documentation
## Document map
### [Domain layer](./domain/README.md)
App-specific behavior and the values it acts on
- [Use case](./domain/UseCase.md)- [Repository](./domain/RepositoryInterface.md)
## Domain layer
App-specific behavior and the values it acts on
- [Use case](./domain/UseCase.md) ... A single app-specific behavior that happens on a screen- [Repository](./domain/RepositoryInterface.md) ... The boundary where data is fetched and saved[myapp](../README.md)
# Domain layer
App-specific behavior and the values it acts on
| Role | Summary ||---|---|| [Use case](./UseCase.md) | A single app-specific behavior that happens on a screen || [Repository](./RepositoryInterface.md) | The boundary where data is fetched and saved |
## Placement in this group
```core/domain/src/main/kotlin/com/example/core/domain/ useCase/*UseCase.kt Use case repository/*Repository.kt Repository```[myapp](../README.md) / [Domain layer](README.md)
# Use case
A single app-specific behavior that happens on a screen
## Placement
| Path | When to use ||---|---|| `core/domain/src/main/kotlin/com/example/core/domain/useCase/*UseCase.kt` | |
## Constraints
- has only an invoke method
## Examples
- `GetItemListUseCase` ... The list of items- `SignInUseCase` ... Signing in[myapp](../README.md) / [Domain layer](README.md)
# Repository
The boundary where data is fetched and saved
## Placement
| Path | When to use ||---|---|| `core/domain/src/main/kotlin/com/example/core/domain/repository/*Repository.kt` | |The samples in katachi’s GitHub repository keep actually generated documentation in sample/<name>/docs/. See them for concrete examples as well.
Output directory
Section titled “Output directory”By default the documentation is generated under build/katachi/docs/.
The destination can be changed by setting katachi.processors.docs.outputDir in build.gradle.kts.
katachi { architecture = "com.example.projectArchitecture"
processors { docs { outputDir = rootProject.layout.projectDirectory.dir("docs/architecture") } }}Generated files
Section titled “Generated files”These are the files generated from an architecture { } definition.
Without groups
Section titled “Without groups”Directory<output directory>/
- README.md
- <Role>.md
With groups
Section titled “With groups”Directory<output directory>/
- README.md
- <Role>.md
Directory<group>/
- README.md
- <Role>.md
Directory<nested group>/
- README.md
- <Role>.md
Groups can nest, and a nested group is generated below the directory of its parent group.
File and directory names are decided by the identifier (UseCase in "UseCase" { }, domain in "domain".group { }), not by title. Changing title does not change the file name.
Properties used to generate the documentation
Section titled “Properties used to generate the documentation”The documentation is generated from the properties you set on Roles and groups.
| Property | What it becomes in the generated documentation | Pages it is used on |
|---|---|---|
title |
The heading of the Role’s page. Defaults to the Role’s identifier | <Role>.md, and the link text in the index and listings |
summary |
The single line that goes in the listing table | <Role>.md, the parent’s README.md |
description |
The body of the Role’s page. The Markdown you write goes in as is, newlines included. katachi adds no structure of its own, so the headings inside are yours to choose | <Role>.md |
example(name, description) |
A concrete example. Call it once per example; they appear in the order you called them | ## Examples in <Role>.md |
documented |
Set to false to keep it out of the documentation. It only stops being documented – it still takes part in assert() and the other checks |
— |
Role definition
"UseCase" { title = "UseCase" summary = "A single app-specific behavior that happens on a screen" description = """ A UseCase is a class representing one app-specific behavior. """.trimIndent()
example("GetItemListUseCase", "Fetches the list of items") example("SignInUseCase", "Signs in")
layout { "src/main/kotlin/com/example/useCase" / "*UseCase".ktFile() }
"has only an invoke method".konsist { /* ... */ }}Generated Markdown
[Architecture](README.md) ← breadcrumb
# UseCase ← title
A single app-specific behavior that happens on a screen ← summary
A UseCase is a class representing one app-specific behavior. ← description
## Placement ← layout { }
| Path | When to use ||---|---|| `src/main/kotlin/com/example/useCase/*UseCase.kt` | |
## Constraints ← the name of konsist { }
- has only an invoke method
## Examples ← example()
- `GetItemListUseCase` ... Fetches the list of items- `SignInUseCase` ... Signs inA group takes the same four properties a Role does. Only example() is missing.
| Property | What it becomes in the generated documentation | Pages it is used on |
|---|---|---|
title |
The heading of the group’s README. Defaults to the group’s identifier | README.md, <group>/README.md |
summary |
The single line in the parent’s document map and in the group’s section | README.md, <group>/README.md |
description |
The body of the group’s README | <group>/README.md |
documented |
Set to false to keep the whole group out of the documentation. It still takes part in the checks |
— |
Careful: documented = false takes the group’s contents out with it
A group with documented = false takes its Roles and nested groups out of the documentation along with it, whatever those Roles write for themselves.
"build".group { documented = false "VersionCatalog" { } // leaves the documentation together with the group}The value itself is not copied onto the Roles and groups inside (each keeps what it wrote). A processor of your own that reads this value should walk up Role.groupPath and look at the parent groups’ values too.
group definition
"domain".group { title = "Domain layer" summary = "App-specific behavior and the values it acts on" description = """ The layer that depends on neither the screens nor the storage. """.trimIndent()
"UseCase" { title = "Use case" summary = "A single app-specific behavior that happens on a screen" layout { "src/main/kotlin/com/example/domain/useCase" / "*UseCase".ktFile() } }
"RepositoryInterface" { title = "Repository" summary = "The boundary where data is fetched and saved" layout { "src/main/kotlin/com/example/domain/repository" / "*Repository".ktFile() } }}Generated Markdown
[Architecture](../README.md) ← breadcrumb
# Domain layer ← title
App-specific behavior and the values it acts on ← summary
The layer that depends on neither the screens nor the storage. ← description
| Role | Summary | ← title and summary of the Roles inside|---|---|| [Use case](./UseCase.md) | A single app-specific behavior that happens on a screen || [Repository](./RepositoryInterface.md) | The boundary where data is fetched and saved |
## Placement in this group ← layout { } of the Roles inside
```src/main/kotlin/com/example/domain/ useCase/*UseCase.kt Use case repository/*Repository.kt Repository```Top page
Section titled “Top page”The heading and description of the top page (the README.md right under the output directory) are set by writing title and description directly inside architecture { }.
| Property | What it becomes in the generated documentation | Pages it is used on |
|---|---|---|
title |
The heading of the top page | README.md, the breadcrumb of every page |
description |
The body right under the heading | README.md |
architecture definition
val projectArchitecture = architecture { title = "myapp" description = """ The file layout of myapp.
myapp is designed on top of **MVVM * Clean Architecture**. """.trimIndent()
"domain".group { title = "Domain layer" summary = "App-specific behavior and the values it acts on" "UseCase" { /* ... */ } }}Generated Markdown
# myapp documentation
The file layout of myapp.
myapp is designed on top of **MVVM * Clean Architecture**.
## Document map
### [Domain layer](./domain/README.md)
App-specific behavior and the values it acts on...Custom sections
Section titled “Custom sections”To add your own sections to the documentation on top of the ones provided by default, such as the title, define a section with documentSection().
Declare a section with documentSection("heading"), and create a property to write it through with by.
val TestPolicy = documentSection("Test policy")var MetadataScope.testPolicy by TestPolicyNesting sections
Sections can be nested. Declare the child on its parent section, as in TestPolicy.documentSection("Unit tests").
The heading level follows from the depth. The top level is ##, and each level down adds one #.
Even if the parent has no body, its heading still appears when a child has one.
Section definition
val TestPolicy = documentSection("Test policy")var MetadataScope.testPolicy by TestPolicy
val UnitTest = TestPolicy.documentSection("Unit tests")var MetadataScope.unitTest by UnitTest
val projectArchitecture = architecture { "UseCase" { title = "Use case" summary = "A single app-specific behavior that happens on a screen" example("SignInUseCase", "Signing in") layout { "src/main/kotlin/com/example/useCase" / "*UseCase".ktFile() }
unitTest = "Replace the repository with a fake and check the return value." }}Generated Markdown
[Architecture](README.md)
# Use case
A single app-specific behavior that happens on a screen
## Placement
| Path | When to use ||---|---|| `src/main/kotlin/com/example/useCase/*UseCase.kt` | |
## Examples
- `SignInUseCase` ... Signing in
## Test policy ← no body, but shown because a child has one
### Unit tests ← one level down, so ###
Replace the repository with a fake and check the return value.Custom sections come after the built-in sections (## Placement / ## Constraints / ## Examples), in the order they are written in the definition.
Summary
Section titled “Summary”- Documentation is created by setting properties such as title, summary, description, and example in the architecture definition.
Next steps
Section titled “Next steps”- Generating code from a template … Learn how to generate code, rather than documentation, from the same definition.
- ArchitectureProcessor and customizing it … Document generation is one ArchitectureProcessor too. Learn how to produce other outputs of your own.