
Interactive architecture analysis and visualization tool for Kotlin projects powered by KSP (Kotlin Symbol Processing).
The project analyzes Kotlin source code during compilation and generates:
The generated reports allow exploring project structure directly in the browser.
The project generates a full interactive architecture website.
Includes:
Supported outputs:
The library generates:
@UmlDiagram
class UserService(
private val repository: UserRepository
)graph TD
UserService --> UserRepositoryThe processor automatically generates:
architecture.html
architecture-snapshot.json
com_example_service.html
UserService.html
with interactive navigation between pages.
Large graphs include a few navigation helpers:
presentation, domain, data, and core in the viewer;Fit Selection zooms to the selected symbol and its visible dependencies;Neighbors moves visible dependencies of the selected symbol closer, which helps inspect long edges in large projects;Reset Layout restores the generated auto-layout.Each package page contains:
Each class page contains:
Kotlin Source Code
│
▼
KSP Processor
│
▼
Static Code Analysis
│
├── Dependency Analysis
├── Package Analysis
├── Module Detection
├── Metrics
└── Cycle Detection
│
▼
Generators
│
├── HTML
├── PlantUML
├── Mermaid
├── JSON
└── Markdown Reports
│
▼
Generated Architecture Viewer
idiomatic-architecture-viewer
│
├── processor
│
├── analysis
│ ├── CycleDetector
│ └── ArchitectureAnalysis
│
├── diagram
│ ├── ArchitectureGraphGenerator
│ ├── PackageDiagramGenerator
│ ├── MetricsReportGenerator
│ └── ModuleDiagramGenerator
│
├── export
│ ├── ArchitectureHtmlExporter
│ ├── PackageHtmlExporter
│ ├── ClassHtmlExporter
│ └── ArchitectureJsonExporter
│
├── generation
│ ├── HtmlGenerationService
│ ├── DiagramGenerationService
│ ├── JsonGenerationService
│ └── UmlClassGenerationService
│
├── metrics
│
├── uml
│
├── writer
│
├── build.gradle.kts
└── settings.gradle.kts
The annotation API is intentionally small. In source code you always use the same annotation:
import uml.UmlDiagram
@UmlDiagram
class UserService
@UmlDiagram
fun UserScreen() {
}The Gradle setup is different for JVM-only and Kotlin Multiplatform projects.
Use the same VERSION for all Idiomatic Architecture Viewer artifacts.
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
implementation(
"io.github.nastyoonaa:idiomatic-architecture-viewer:VERSION"
)
ksp(
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
}Use the multiplatform annotations artifact from commonMain. The KSP processor
still runs only during Gradle compilation.
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.google.devtools.ksp")
}
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation(
"io.github.nastyoonaa:idiomatic-architecture-viewer-annotations:VERSION"
)
}
}
}
dependencies {
add(
"kspCommonMainMetadata",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspAndroid",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosX64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosArm64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosSimulatorArm64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
}For Kotlin Multiplatform, the generated static viewer appears under the KSP output of each target, for example:
build/generated/ksp/metadata/commonMain/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/android/androidDebug/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/iosX64/iosX64Main/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/iosSimulatorArm64/iosSimulatorArm64Main/resources/com/example/generated/architecture/architecture.html
Annotate classes, objects, interfaces, or top-level functions:
import uml.UmlDiagram
@UmlDiagram
class UserService(
private val repository: UserRepository
)
@UmlDiagram
fun UserScreen(
service: UserService
) {
}Top-level functions are shown as function nodes in architecture.html. This
also works for Compose functions and KMP expect/actual functions when they
are visible to the KSP task being executed.
Import dependencies are also shown. If an annotated class or function imports a project class that is not annotated, the viewer can still create an imported class node and render the dependency.
Build the project:
./gradlew buildGenerated files will appear in:
build/generated/ksp/
Open architecture.html in a browser to inspect the generated static viewer.
The processor also generates architecture-snapshot.json. Keep this file from
a previous build if you want to compare it with a newer architecture report.
Typical flow:
architecture-snapshot.json somewhere safe, for example in CI
artifacts or release artifacts.architecture.html.architecture.html.Snapshot.architecture-snapshot.json.The viewer will compare the old snapshot with the current full graph and show:
Snapshot comparison uses the serialized architecture model, not the current UI state. Filters, selected nodes, zoom, and layout changes do not affect the snapshot diff.
The snapshot file has this shape:
{
"schemaVersion": 1,
"generatedBy": "idiomatic-architecture-viewer",
"data": {
"nodes": [],
"edges": [],
"tree": [],
"summary": {},
"report": {}
}
}If the snapshot schema is unsupported or the file is invalid, the viewer shows an error and keeps the current architecture graph unchanged.
The static viewer can also load an optional call graph layer from an external
architecture-callgraph.json file.
This is intentionally separate from the KSP structural graph. The default KSP processor stays lightweight and continues to generate only structural architecture data such as declarations, imports, constructors, properties, method parameters, return types, and inheritance.
Typical flow:
architecture-callgraph.json file from an external
call graph analyzer.architecture.html.Call Graph.architecture-callgraph.json.Call edge type in the toolbar.The viewer merges the call graph layer in memory only:
ViewerData is not modifiedarchitecture-snapshot.json is not modifiedThe minimal call graph file shape is:
{
"schemaVersion": 1,
"generatedBy": "idiomatic-architecture-viewer-callgraph",
"nodes": [
{
"id": "com.example.UserViewModel.loadUser()",
"label": "UserViewModel.loadUser()",
"pkg": "com.example",
"module": "app",
"sourceSet": "commonMain",
"file": "UserViewModel.kt",
"kind": "function",
"layer": "presentation"
}
],
"edges": [
{
"from": "com.example.UserViewModel.loadUser()",
"to": "com.example.GetUserUseCase.invoke()",
"type": "call",
"context": "method-body",
"snippet": "getUserUseCase.invoke()"
}
]
}Only the minimum graph data is loaded. The viewer does not store AST, IR, method bodies, runtime traces, reflection data, or compiler dumps in the snapshot.
Unknown call targets can be represented with stable unresolved ids:
unresolved:call:externalAnalytics.trackImport
Call graph support is a visualization overlay. It does not currently make the KSP processor perform semantic method-body analysis.
For demos, present this as a prepared extension point rather than a completed semantic call graph analyzer:
architecture-callgraph.json
Examples:
architecture.html
architecture.json
architecture-snapshot.json
ArchitectureOverview.puml
ArchitectureMetrics.md
DependencyCycles.md
ArchitectureGraph.puml
com_example_service.html
UserService.html
graph TD
UserService --> UserRepository
UserRepository --> Database@startuml
class UserService
class UserRepository
UserService --> UserRepository
@endumlPlanned features:
Artifacts are published to Maven Central.
Published artifacts:
io.github.nastyoonaa:idiomatic-architecture-viewer
io.github.nastyoonaa:idiomatic-architecture-viewer-processor
io.github.nastyoonaa:idiomatic-architecture-viewer-annotations
Release CI validates the project first and then publishes all configured Maven Central publications.
Anastasia Tsipenyuk
GitHub: https://github.com/Nastyoonaa
Telegram: @Iydyshka_krovopivyshka
Interactive architecture analysis and visualization tool for Kotlin projects powered by KSP (Kotlin Symbol Processing).
The project analyzes Kotlin source code during compilation and generates:
The generated reports allow exploring project structure directly in the browser.
The project generates a full interactive architecture website.
Includes:
Supported outputs:
The library generates:
@UmlDiagram
class UserService(
private val repository: UserRepository
)graph TD
UserService --> UserRepositoryThe processor automatically generates:
architecture.html
architecture-snapshot.json
com_example_service.html
UserService.html
with interactive navigation between pages.
Large graphs include a few navigation helpers:
presentation, domain, data, and core in the viewer;Fit Selection zooms to the selected symbol and its visible dependencies;Neighbors moves visible dependencies of the selected symbol closer, which helps inspect long edges in large projects;Reset Layout restores the generated auto-layout.Each package page contains:
Each class page contains:
Kotlin Source Code
│
▼
KSP Processor
│
▼
Static Code Analysis
│
├── Dependency Analysis
├── Package Analysis
├── Module Detection
├── Metrics
└── Cycle Detection
│
▼
Generators
│
├── HTML
├── PlantUML
├── Mermaid
├── JSON
└── Markdown Reports
│
▼
Generated Architecture Viewer
idiomatic-architecture-viewer
│
├── processor
│
├── analysis
│ ├── CycleDetector
│ └── ArchitectureAnalysis
│
├── diagram
│ ├── ArchitectureGraphGenerator
│ ├── PackageDiagramGenerator
│ ├── MetricsReportGenerator
│ └── ModuleDiagramGenerator
│
├── export
│ ├── ArchitectureHtmlExporter
│ ├── PackageHtmlExporter
│ ├── ClassHtmlExporter
│ └── ArchitectureJsonExporter
│
├── generation
│ ├── HtmlGenerationService
│ ├── DiagramGenerationService
│ ├── JsonGenerationService
│ └── UmlClassGenerationService
│
├── metrics
│
├── uml
│
├── writer
│
├── build.gradle.kts
└── settings.gradle.kts
The annotation API is intentionally small. In source code you always use the same annotation:
import uml.UmlDiagram
@UmlDiagram
class UserService
@UmlDiagram
fun UserScreen() {
}The Gradle setup is different for JVM-only and Kotlin Multiplatform projects.
Use the same VERSION for all Idiomatic Architecture Viewer artifacts.
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
implementation(
"io.github.nastyoonaa:idiomatic-architecture-viewer:VERSION"
)
ksp(
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
}Use the multiplatform annotations artifact from commonMain. The KSP processor
still runs only during Gradle compilation.
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.google.devtools.ksp")
}
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation(
"io.github.nastyoonaa:idiomatic-architecture-viewer-annotations:VERSION"
)
}
}
}
dependencies {
add(
"kspCommonMainMetadata",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspAndroid",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosX64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosArm64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
add(
"kspIosSimulatorArm64",
"io.github.nastyoonaa:idiomatic-architecture-viewer-processor:VERSION"
)
}For Kotlin Multiplatform, the generated static viewer appears under the KSP output of each target, for example:
build/generated/ksp/metadata/commonMain/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/android/androidDebug/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/iosX64/iosX64Main/resources/com/example/generated/architecture/architecture.html
build/generated/ksp/iosSimulatorArm64/iosSimulatorArm64Main/resources/com/example/generated/architecture/architecture.html
Annotate classes, objects, interfaces, or top-level functions:
import uml.UmlDiagram
@UmlDiagram
class UserService(
private val repository: UserRepository
)
@UmlDiagram
fun UserScreen(
service: UserService
) {
}Top-level functions are shown as function nodes in architecture.html. This
also works for Compose functions and KMP expect/actual functions when they
are visible to the KSP task being executed.
Import dependencies are also shown. If an annotated class or function imports a project class that is not annotated, the viewer can still create an imported class node and render the dependency.
Build the project:
./gradlew buildGenerated files will appear in:
build/generated/ksp/
Open architecture.html in a browser to inspect the generated static viewer.
The processor also generates architecture-snapshot.json. Keep this file from
a previous build if you want to compare it with a newer architecture report.
Typical flow:
architecture-snapshot.json somewhere safe, for example in CI
artifacts or release artifacts.architecture.html.architecture.html.Snapshot.architecture-snapshot.json.The viewer will compare the old snapshot with the current full graph and show:
Snapshot comparison uses the serialized architecture model, not the current UI state. Filters, selected nodes, zoom, and layout changes do not affect the snapshot diff.
The snapshot file has this shape:
{
"schemaVersion": 1,
"generatedBy": "idiomatic-architecture-viewer",
"data": {
"nodes": [],
"edges": [],
"tree": [],
"summary": {},
"report": {}
}
}If the snapshot schema is unsupported or the file is invalid, the viewer shows an error and keeps the current architecture graph unchanged.
The static viewer can also load an optional call graph layer from an external
architecture-callgraph.json file.
This is intentionally separate from the KSP structural graph. The default KSP processor stays lightweight and continues to generate only structural architecture data such as declarations, imports, constructors, properties, method parameters, return types, and inheritance.
Typical flow:
architecture-callgraph.json file from an external
call graph analyzer.architecture.html.Call Graph.architecture-callgraph.json.Call edge type in the toolbar.The viewer merges the call graph layer in memory only:
ViewerData is not modifiedarchitecture-snapshot.json is not modifiedThe minimal call graph file shape is:
{
"schemaVersion": 1,
"generatedBy": "idiomatic-architecture-viewer-callgraph",
"nodes": [
{
"id": "com.example.UserViewModel.loadUser()",
"label": "UserViewModel.loadUser()",
"pkg": "com.example",
"module": "app",
"sourceSet": "commonMain",
"file": "UserViewModel.kt",
"kind": "function",
"layer": "presentation"
}
],
"edges": [
{
"from": "com.example.UserViewModel.loadUser()",
"to": "com.example.GetUserUseCase.invoke()",
"type": "call",
"context": "method-body",
"snippet": "getUserUseCase.invoke()"
}
]
}Only the minimum graph data is loaded. The viewer does not store AST, IR, method bodies, runtime traces, reflection data, or compiler dumps in the snapshot.
Unknown call targets can be represented with stable unresolved ids:
unresolved:call:externalAnalytics.trackImport
Call graph support is a visualization overlay. It does not currently make the KSP processor perform semantic method-body analysis.
For demos, present this as a prepared extension point rather than a completed semantic call graph analyzer:
architecture-callgraph.json
Examples:
architecture.html
architecture.json
architecture-snapshot.json
ArchitectureOverview.puml
ArchitectureMetrics.md
DependencyCycles.md
ArchitectureGraph.puml
com_example_service.html
UserService.html
graph TD
UserService --> UserRepository
UserRepository --> Database@startuml
class UserService
class UserRepository
UserService --> UserRepository
@endumlPlanned features:
Artifacts are published to Maven Central.
Published artifacts:
io.github.nastyoonaa:idiomatic-architecture-viewer
io.github.nastyoonaa:idiomatic-architecture-viewer-processor
io.github.nastyoonaa:idiomatic-architecture-viewer-annotations
Release CI validates the project first and then publishes all configured Maven Central publications.
Anastasia Tsipenyuk
GitHub: https://github.com/Nastyoonaa
Telegram: @Iydyshka_krovopivyshka