コンテンツにスキップ
  • 初心者向け

モチベーションと katachi の立ち位置

katachi の全体像を掴むための、5分で読めるスライドもあります。

アーキテクチャドキュメントは必ず実装と乖離する

Section titled “アーキテクチャドキュメントは必ず実装と乖離する”

優秀なあなたのことですから、プロジェクトには /docs ディレクトリがあり、プロジェクト全体の構造や各コンポーネントのルールが詳しく書かれていることでしょう。

しかし、ドキュメントに書かれた「UseCase はこう書く」という文言には、何の強制力もありません。プロジェクトのルールを知らない新規参入者や、振る舞いに不確実さのある AI Agent は、そのルールをたやすく破りかねません。

ルールを無視したコードを通さないガードレールとして働くツールの1つに、Konsist があります。

Konsist はプロジェクトのルールを Unit test として記述できます。

class UseCaseKonsistTest {
@Test
fun `UseCase は 1 つの invoke 関数のみを持つ`() {
Konsist
.scopeFromProject()
.classes()
.withNameEndingWith("UseCase")
.assertTrue { it.functions().map { function -> function.name } == listOf("invoke") }
}
}

一見すると、Konsist があればプロジェクトのルールを十分に強制できそうです。 しかし実際には、Konsist のある特性のために、それは難しいのです。

特定のファイルに絞って見るだけでなく、プロジェクト全体を見渡して検証する手段が必要 です。

katachi では、プロジェクト全体を見渡して、現れてよいファイルの種類とその置き方を宣言できます。

val projectArchitecture = architecture {
"UseCase" {
layout {
"src/main/kotlin/com/example/project/useCase" / "*UseCase".ktFile()
}
}
}

katachi では次の2つを宣言します。

  • プロジェクトの中に現れて良いファイルの種類(Role と呼ばれます)
  • その Role のファイルがどこに現れて良いか

これを宣言的な DSL で書くと、プロジェクト全体のディレクトリ構造をテストできるようになります。

このルールによって検知される例

もし AI Agent が com.example.project.GetUserUseCase という名前のファイルを配置した場合:

com/example/project/GetUserUseCase.kt
package com.example.project // ❌ 本来は com.example.project.useCase に置くべきです
class GetUserUseCase {
suspend fun invoke() = ...
}
  • ディレクトリsrc/main/kotlin/com/example/project/
    • GetUserUseCase.kt ❌ 宣言されていない場所に置かれた
    • ディレクトリuseCase/
      • UpdateUserUseCase.kt ⭐️ 宣言どおりの場所

katachi を含んだ Unit test を実行すると、以下のようにエラーが報告されます。

Katachi check failed: 1 violation (Unexpected: 1)
[UnexpectedFile] file:///path/to/project/src/main/kotlin/com/example/project/GetUserUseCase.kt
No role is defined for this file.
Nearby locations:
UseCase file:///path/to/project/src/main/kotlin/com/example/project/useCase/
How to fix:
- Move it to one of the locations above
- Delete it if it is not needed
- Add a new role for it:
"GetUserUseCase" {
summary = "TODO"
layout {
"src/main/kotlin/com/example/project" / "GetUserUseCase".ktFile()
}
}

katachi は、アーキテクチャ定義からさまざまなものを生成できます。

ドキュメントを生成する

katachi で書いたプロジェクト構造の定義から、markdown のドキュメントを生成できます。

ターミナルウィンドウ
$ ./gradlew katachiDocs

Gradle plugin の katachi { architecture = "..." } で定義の場所を指定しておくと、定義を置いたモジュールの build/katachi/docs に書き出されます。

アーキテクチャ定義

ProjectArchitecture.kt
val projectArchitecture = architecture {
"UseCase" {
title = "ユースケース"
summary = "各画面で発生するアプリ固有の1つの振る舞い"
example("GetUserUseCase", "ユーザーを取得する")
example("UpdateUserUseCase", "ユーザーを更新する")
layout {
"src/main/kotlin/com/example/project/useCase" {
description = "複数の画面から使われるもの"
"*UseCase".ktFile()
}
}
}
// ...
}

生成された Markdown

build/katachi/docs/UseCase.md
[Architecture](README.md)
# ユースケース
各画面で発生するアプリ固有の1つの振る舞い
## Placement
| Path | When to use |
|---|---|
| `src/main/kotlin/com/example/project/useCase/*UseCase.kt` | 複数の画面から使われるもの |
## Examples
- `GetUserUseCase` ... ユーザーを取得する
- `UpdateUserUseCase` ... ユーザーを更新する

katachi で定義したプロジェクト構造を SSoT にして、人間も AI Agent も読めるドキュメントを生成しましょう。

詳しくは ドキュメント生成 をご覧ください。

テンプレートからコード生成する

アーキテクチャ定義の、ファイルの宣言そのものに典型的なソースコードのテンプレートを付けると、そのファイルをコマンド1つで生成できます。

ターミナルウィンドウ
$ ./gradlew katachiTemplate --arg template=UseCase,UseCaseImplementation --arg name=GetUser

アーキテクチャ定義

ProjectArchitecture.kt
val projectArchitecture = architecture {
"UseCase" {
layout {
"src/main/kotlin/com/example/project/useCase" /
"${capture("name")}UseCase".ktFile()
.template {
val name = captureValue("name")
"""
package com.example.project.useCase
interface ${name}UseCase {
suspend operator fun invoke()
}
""".trimIndent()
}
}
}
"UseCaseImplementation" {
layout {
"src/main/kotlin/com/example/project/useCase" /
"${capture("name")}UseCaseImpl".ktFile()
.template {
val name = captureValue("name")
"""
package com.example.project.useCase
class ${name}UseCaseImpl : ${name}UseCase {
override suspend fun invoke() {
// ここに振る舞いを書く
}
}
""".trimIndent()
}
}
}
// ...
}

生成するコマンドと生成されたソースコード

ターミナルウィンドウ
$ ./gradlew katachiTemplate --arg template=UseCase,UseCaseImplementation --arg name=GetUser
[1/3] Processors: template
[2/3] Processing...
[template] Generating 2 files under file:///path/to/project
[template] Wrote file:///path/to/project/src/main/kotlin/com/example/project/useCase/GetUserUseCase.kt
[template] Wrote file:///path/to/project/src/main/kotlin/com/example/project/useCase/GetUserUseCaseImpl.kt
========================================
[3/3] Katachi processor run: 1 succeeded, 0 failed
[OK] template
========================================
src/main/kotlin/com/example/project/useCase/GetUserUseCase.kt
package com.example.project.useCase
interface GetUserUseCase {
suspend operator fun invoke()
}

詳しくは テンプレートからコード生成 をご覧ください。

Konsist と連携すると、katachi でプロジェクト全体を検証したあとに、個々のファイルを別々に検証できます。

val projectArchitecture = architecture {
"UseCase" {
// どこに置いてよいか
layout {
"src/main/kotlin/com/example/project/useCase" / "*UseCase".ktFile()
}
// 中身がどうあるべきか
"1 つの invoke 関数のみを持つこと".konsist {
classes()
.withNameEndingWith("UseCase")
.must { it.functions().map { function -> function.name } == listOf("invoke") }
}
}
}

テストで assert(FileConstraintCheck()) を呼ぶと、配置の違反と中身の違反が、同じ1つのレポートに並びます。

katachi でプロジェクト全体の構造をテストし、そのあと Konsist で各ファイルの中身を検証する という使い分けをおすすめします。

詳しくは Konsist との統合 をご覧ください。

  • AI を使った開発では、プロジェクト構造のルールを AI Agent に守らせる手段 が必要です。
  • katachi はプロジェクト全体の構造を定義・テストしたり、ドキュメントを生成したりする DSL を提供します。
  • プロジェクト全体の構造は katachi、各ファイルの中身は Konsist。役割が違うので、併用するのがおすすめの使い方です。
  • 初めてのアーキテクチャ定義 … あなたのプロジェクトに katachi のアーキテクチャ定義を導入して、その有用性を実感しましょう。
  • FAQ … katachi の設計思想をよくある質問を通して理解しましょう。
  • Role の定義 … katachi でアーキテクチャ定義を行う基本単位である Role について学びましょう。
  • Layout … katachi でディレクトリ構造を定義するための Layout について学びましょう。