Basic API
This page introduces katachi’s core API. Once you know these, you should understand the basics of using katachi.
1. architecture { }
Section titled “1. architecture { }”architecture { } is the entry point for defining your project’s overall architecture.
Inside it, you write the roles of the files that exist in your project.
architecture { "UseCase" { /* ... */ } "RepositoryInterface" { /* ... */ }}// This project has two roles: `UseCase` and `RepositoryInterface`The elements inside an architecture (Role) are covered in more depth in a later section.
2. Testing the architecture with assert()
Section titled “2. Testing the architecture with assert()”assert() is the function that tests the architecture defined with architecture { }.
val projectArchitecture = architecture { "UseCase" { /* ... */ } "RepositoryInterface" { /* ... */ }}
@Testfun `files are placed according to the architecture`() { projectArchitecture.assert()}3. Declaring a Role with "Name" { }
Section titled “3. Declaring a Role with "Name" { }”"Name" { } declares the role that a file appearing in the project plays.
architecture { "UseCase" { /* ... */ }
"RepositoryInterface" { /* ... */ }}Writing "Name" { } inside an architecture { } block lets you declare the roles of files that appear in the project.
Example roles
Section titled “Example roles”If you’re using a well-known architecture, roles typically look something like this.
| Architecture | Role |
|---|---|
| MVC | Controller |
View |
|
Model |
|
| MVVM | View |
ViewModel |
|
| Clean architecture | Service |
UseCase |
|
Entity |
|
RepositoryInterface, RepositoryImplementation |
Deny by default
Section titled “Deny by default”In katachi, only files with a declared Role are allowed to exist in the project.
In other words, adopting katachi requires you to enumerate the Roles that match your project’s conventions.
Related documentation
Section titled “Related documentation”For Role, also see Role definitions in depth.
4. Grouping Roles with "...".group { }
Section titled “4. Grouping Roles with "...".group { }”"Name".group { } is a block for grouping Roles together.
For example, in Clean architecture, UseCase and RepositoryInterface are both roles in the same domain layer.
architecture { "domain".group { "UseCase" { /* ... */ } "RepositoryInterface" { /* ... */ } }}Expressing a group like “the domain layer” in the DSL makes the relationships and hierarchy between roles clear. Grouping Roles at a sensible granularity is important for building an architecture definition that’s easy to understand.
Related documentation
Section titled “Related documentation”For grouping Roles, also see Role definitions in depth.
5. Defining where files may exist with layout { }
Section titled “5. Defining where files may exist with layout { }”Declaring a Role with "..." { } alone doesn’t decide where that Role’s files may be placed.
layout { } is the block that defines where the files for a Role may be placed.
architecture { "UseCase" { layout { "src/main/kotlin/com/example/useCase" / "*UseCase".ktFile() } }}
// A UseCase may be placed at `src/main/kotlin/com/example/useCase/*UseCase.kt`How it works
Section titled “How it works”Under the Deny by default principle, you can’t place a file anywhere that isn’t declared in layout { }.
Say an AI Agent tries to place a new UseCase file at src/main/kotlin/com/example/GetItemUseCase.kt (it should really go inside the useCase package). The moment assert() is called, katachi detects that no Role matches this path, and the test fails.
Directorysrc/main/kotlin/com/example/
- GetItemUseCase.kt ❌ a place not declared in
layout { } DirectoryuseCase/
- GetUserUseCase.kt ⭐️ where the declaration puts it
- GetItemUseCase.kt ❌ a place not declared in
Related documentation
Section titled “Related documentation”For layout { }, also see Layout definitions in depth.
6. Constraining a file’s contents with "...".konsist { }
Section titled “6. Constraining a file’s contents with "...".konsist { }”You can set constraints on the files of a declared Role using Konsist.
architecture { "UseCase" { layout { "src/main/kotlin/com/example/useCase" / "*UseCase".ktFile() }
"has exactly one invoke function".konsist { classes().must { it.functions().map { f -> f.name } == listOf("invoke") } } }}The constraints apply to the files specified in layout { }, and the constraints themselves are written with the Konsist API.
To check the constraints, call assert(FileConstraintCheck()) instead of assert() in the test. With assert() alone the constraints are not evaluated, and the test fails with [UncheckedFileConstraint].
Related documentation
Section titled “Related documentation”For "...".konsist { }, also see the Konsist integration guide.
Summary
Section titled “Summary”| API | What it does | Example |
|---|---|---|
| The function that serves as the entry point for defining your project’s overall architecture. |
|
| Checks whether files are placed according to the definition. It’s a single line called from a test — without it, nothing happens. |
|
| Declares the role (Role) of a file that appears in the project. |
|
| Groups Roles together to build a hierarchy. Optional — Roles work fine without it. |
|
| Defines where that Role’s files may be placed. |
|
| Constrains the contents of that Role’s files. Requires the separate |
|
Next steps
Section titled “Next steps”- Your first architecture definition … Introduce katachi into your project and actually write these six things.
- Role … Learn how to write descriptions for roles, and how to keep the definition from becoming a copy of your directory structure.
- Layout … Learn how to write out placements, and when to reach for the deny-by-default escape hatches
anyFile()/ignore().