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

基本的な API

katachi の主要な API を紹介します。 ここに挙げる API を覚えれば、katachi の基本的な使い方を理解できるはずです。

architecture { } はプロジェクト全体のアーキテクチャを定義するエントリーポイントです。 このブロックの中に、プロジェクトにあるファイルの Role を書きます。

architecture {
"UseCase" { /* ... */ }
"RepositoryInterface" { /* ... */ }
}
// このプロジェクトには `UseCase` と `RepositoryInterface` という2つの Role がある

アーキテクチャを構成する要素 (Role) は、後のセクションで詳しく説明します。

2. アーキテクチャをテストする assert()

Section titled “2. アーキテクチャをテストする assert()”

architecture { } で定義したアーキテクチャをテストする関数が assert() です。

val projectArchitecture = architecture {
"UseCase" { /* ... */ }
"RepositoryInterface" { /* ... */ }
}
@Test
fun `アーキテクチャに沿ったファイル配置になっている`() {
projectArchitecture.assert()
}

"名前" { } はプロジェクトの中に登場するファイルの役割 (Role) を定義します。

architecture {
"UseCase" { /* ... */ }
"RepositoryInterface" { /* ... */ }
}

architecture { } ブロックの中に "名前" { } を書くと、プロジェクトに登場するファイルの Role を定義できます。

よく知られたアーキテクチャを採用しているなら、Role はたとえば次のようになるでしょう。

アーキテクチャ Role
MVC Controller
View
Model
MVVM View
ViewModel
Clean architecture Service
UseCase
Entity
RepositoryInterface, RepositoryImplementation

katachi では、宣言した Role のファイルしかプロジェクトに存在を許されません。

つまり katachi を導入するときは、プロジェクトの規約に合った Role を列挙する必要があります。

Role については Role 定義の詳細 も参照してください。

4. Role をグルーピングする "...".group { }

Section titled “4. Role をグルーピングする "...".group { }”

"名前".group { } は Role をまとめるためのブロックです。

例えば Clean architecture では、UseCase と RepositoryInterface は 同じドメインレイヤーの Role です。

architecture {
"domain".group {
"UseCase" { /* ... */ }
"RepositoryInterface" { /* ... */ }
}
}

Role のグループをドメインレイヤーとして DSL で表すと、Role 同士の関係や階層構造が明確になります。分かりやすいアーキテクチャ定義を作るには、分かりやすい粒度で Role をグループ化することが大切です。

Role のグループについては Role 定義の詳細 も参照してください。

5. ファイルが存在して良い位置を定義する layout { }

Section titled “5. ファイルが存在して良い位置を定義する layout { }”

"..." { } で Role を定義しただけでは、その Role のファイルをどこに置いてよいかは決まりません。

layout { } は Role に対応するファイルをどこに置けるかを定義するためのブロックです。

architecture {
"UseCase" {
layout {
"src/main/kotlin/com/example/useCase" / "*UseCase".ktFile()
}
}
}
// UseCase は `src/main/kotlin/com/example/useCase/*UseCase.kt` に置くことができる

Deny by default の原則により、layout { } で宣言していない場所にはファイルを置けません。

たとえば AI Agent が、新しい UseCase ファイルを src/main/kotlin/com/example/GetItemUseCase.kt に置こうとしたとします (本来は useCase パッケージの中に置くべきです)。 assert() が呼び出された時点で、katachi はこのパスに一致する Role が無いことを検知し、テストが失敗します。

  • ディレクトリsrc/main/kotlin/com/example/
    • GetItemUseCase.kt ❌ layout { } で宣言していない場所
    • ディレクトリuseCase/
      • GetUserUseCase.kt ⭐️ 宣言どおりの場所

layout { } については Layout 定義の詳細 も参照してください。

6. ファイル内の制約を定義する "...".konsist { }

Section titled “6. ファイル内の制約を定義する "...".konsist { }”

Role を定義したファイルには、Konsist で制約をかけられます。

architecture {
"UseCase" {
layout {
"src/main/kotlin/com/example/useCase" / "*UseCase".ktFile()
}
"invoke 関数を1つだけ持つこと".konsist {
classes().must { it.functions().map { f -> f.name } == listOf("invoke") }
}
}
}

制約の対象は layout { } で指定したファイルで、制約そのものは Konsist の API で書きます。

制約を検査するには、テストで assert() の代わりに assert(FileConstraintCheck()) を呼びます。assert() だけでは制約が評価されず、[UncheckedFileConstraint] としてテストが落ちます。

"...".konsist { } については Konsist 連携ガイド も参照してください。

API何をするか例
architecture { }

プロジェクト全体のアーキテクチャを定義する、エントリーポイントの関数です。

architecture {
"UseCase" { /* ... */ }
}
  • 意味: このプロジェクトには UseCase という Role がある
assert()

定義どおりにファイルが置かれているかを検査します。テストから呼ぶ1行で、これが無いと何も起きません。

@Test
fun `配置が定義どおり`() {
projectArchitecture.assert()
}
  • 意味: 違反があればテストが落ちる。1回の実行で全部の違反を集め、1つのメッセージにまとめる
"名前" { }

プロジェクトの中に登場するファイルの役割 (Role) を定義します。

"UseCase" {
layout { /* ... */ }
}
  • 意味: UseCase という Role がある。宣言していない Role のファイルは置けない
"名前".group { }

Role をまとめて階層を作ります。使うかどうかは任意で、使わなくても Role は書けます。

"domain".group {
"UseCase" { /* ... */ }
}
  • 意味: UseCase は domain レイヤーの Role。修飾名は domain.UseCase になる
layout { }

その Role のファイルをどこに置いてよいかを定義します。

layout {
"domain/useCase" /
"*UseCase".ktFile()
}
  • 意味: domain/useCase/*UseCase.kt にだけ置ける。それ以外の場所に置くと違反になる
"名前".konsist { }

その Role のファイルの中身に制約をかけます。別モジュール katachi-konsist と、テストでの assert(FileConstraintCheck()) が必要です。

"invoke を1つだけ持つこと".konsist {
classes().must { /* 条件 */ }
}
  • 意味: 満たさないファイルがあると違反になる。制約名がそのまま違反レポートに出る
  • 初めてのアーキテクチャ定義 … あなたのプロジェクトに katachi を導入して、この6つを実際に書いてみましょう。
  • Role … Role に説明を書く方法と、定義がディレクトリ構造のコピーにならないようにする考え方を学びましょう。
  • Layout … 置き場所の書き分けと、anyFile() / ignore() という deny by default の逃げ道の使いどころを学びましょう。