Skip to content

katachi

Kotlin architecture test

Architecture docs that run,
with katachi

A file you never declared is not allowed to exist. Write what may go where across the whole project in a Kotlin DSL, and verify it as a test.

ProjectArchitecture.kt
val projectArchitecture = architecture {
"domain".group {
"UseCase" {
layout {
"core/domain" / mainSourceSet / kotlin /
"com/example/core/domain/useCase" / "*UseCase".ktFile()
}
}
}
}

A file in the wrong place is easy to miss in review. And when an AI agent writes one, what it imitates is not your documentation — it is the placement already in the tree. One file off, and it gets copied from there.

Documentation has no way to enforce itself.

src/main/kotlin/com/example/

├─ useCase/
│ ├─ GetUserUseCase.kt
│ └─ UpdateUserUseCase.kt
├─ DeleteUserUseCase.kt ← in the wrong place
└─ misc/
└─ memo.md ← has no role
Nobody is checking these two

What ProjectArchitecture.kt allows

src/main/kotlin/com/example/
├─ useCase/*UseCase.kt ✓ declared
├─ repository/*Repository.kt ✓ declared
├─ DeleteUserUseCase.kt ✕ reported
└─ misc/memo.md ✕ reported
Declared   Reported as a violation

In katachi you declare what may exist. You do not enumerate what is forbidden.

Deny by default — anything not in the declaration cannot exist.

Even when one role lives in two modules, the explanation stays in one place. Declare its placements by listing layout { } blocks.

"UseCase" {
summary = "A single app-specific behavior that occurs on a screen"
layout { "core/domain/useCase" / "*UseCase".ktFile() }
layout { "feature/home/useCase" / "*UseCase".ktFile() }
}

What gets through to an agent is a failing test, not prose.

Define the structure of the whole project and run the test. Any file that is not declared is reported as a violation.