Skip to main content

Architectural audit tool for multi-module Gradle Android projects.

Project description

๐Ÿงฑ modulith-android

Architectural audit for multi-module Gradle Android projects โ€” cycles, layer violations, visibility issues, unused dependencies, and impact cascades, all in a single self-contained HTML report.

Python 3.10+ License: MIT Platform Status


Point it at any Gradle Android repo. It parses your settings.gradle[.kts], walks every module's build file, scans your Kotlin/Java sources for cross-module imports, and produces a single self-contained HTML file with:

  • ๐Ÿ•ธ๏ธ Module graph โ€” every :layer:name module and its dependency edges, colour-coded by layer.
  • ๐Ÿ” Cycle detection โ€” Tarjan-based strongly-connected components; any cyclic coupling between modules is flagged.
  • ๐Ÿšซ Layer violations โ€” edges that cross a forbidden layer pair (e.g. core โ†’ feature under Clean Architecture), with the offending Gradle dependency listed.
  • ๐Ÿ‘๏ธ Visibility audit โ€” for the modules you choose, every public symbol is classified as internalize_candidate, di_required, external_consumer, or star_import_uncertain so you know what's actually load-bearing.
  • ๐Ÿชฆ Unused module dependencies โ€” flags project(":foo") declarations whose target module's packages the consumer's source never imports. Variant-aware: build types, product flavors, and variant combinations (preprodDebugImplementation, debugImplementation, ...) are scanned against the matching source sets, not just src/main.
  • ๐Ÿ’ฅ Impact cascades โ€” pick a module, see every module that transitively depends on it.
  • ๐Ÿ“Š Rankings โ€” modules sorted by fan-in, fan-out, instability, and abstractness.

Configuration-driven and project-agnostic. Every project-specific value (root package, layer names, forbidden layer pairs, audited modules) lives in a single modulith.toml at your repo root. Run modulith-android init and the wizard auto-detects sensible defaults in seconds โ€” confirm with Enter, edit otherwise. Drop it into any Gradle Android project and you're scanning in under a minute.

A report generator, not a CI gate. modulith-android never fails a build and never exits non-zero based on findings. It reads, computes, and writes โ€” interpretation and policy belong to whoever reads the report. By design.


๐Ÿšง Status

Pre-alpha. v0.1.0 in active development. The repository scaffold is in place; CLI, wizard, and analysis pipeline land progressively as the milestones in the roadmap ship. The quick start below describes the shipped behaviour at v0.1.0.


๐Ÿš€ Quick start

# one-shot, no install โ€” needs uv (https://docs.astral.sh/uv/)
uvx --from modulith-android modulith-android init   # wizard writes ./modulith.toml
uvx --from modulith-android modulith-android        # writes build/dependency-graph/index.html

Prefer an installed entry-point? Use pipx:

pipx install modulith-android
modulith-android init
modulith-android

The default run writes build/dependency-graph/index.html. Open it in any browser โ€” no static server, no JavaScript build, no external assets.


๐Ÿ› ๏ธ Commands

Command What it does Output
modulith-android init runs the interactive wizard with auto-detected defaults ./modulith.toml
modulith-android runs the full analysis using ./modulith.toml build/dependency-graph/index.html
modulith-android --repo PATH analyse a project in a different directory report in that project's build/
modulith-android --out PATH write the report somewhere else HTML at the supplied path
modulith-android --config PATH use a config file at a non-default location unchanged
modulith-android --quiet suppress per-phase progress lines on stderr unchanged

Exit codes are 0 on success and 1 on configuration / I/O errors. Findings never change the exit code.


๐Ÿช„ The wizard

modulith-android init runs four short questions, each with an auto-detected default. Confirm with Enter; edit anything that looks off.

# Question Auto-detected from
1 Root package (e.g. com.example) Scans .kt/.java files, computes the longest common package prefix.
2 Layer names (e.g. core, data, feature, app) Reads settings.gradle[.kts], extracts the first segment of each module path.
3 Modules to audit for visibility Pre-selects all modules in the innermost layer; multi-select to customise.
4 Forbidden layer pairs Offers presets (Clean Architecture, MVI/MVVM) or "Custom" for manual entry.

The wizard prints the generated modulith.toml for review and asks before writing.


โš™๏ธ Configuration

The full modulith.toml schema:

# Generated by `modulith-android init`. Hand-edit anything you like.

[scan]
# Root Java/Kotlin package. Used by the visibility analyzer to scope
# import-index scanning to symbols declared in this package tree.
root_package = "com.example"

[layers]
# Ordered list of layer names, derived from the first segment of each
# Gradle module path (e.g. ":core:logger" -> "core"). Modules whose
# first segment isn't listed are parsed for edges but excluded from
# violation checking.
names = ["core", "data", "feature", "app"]

[visibility]
# Gradle module paths whose public API will be enumerated and
# cross-checked for external references.
target_modules = [
    ":core:logger",
    ":core:network",
]

# Each [[violations]] entry forbids a directed edge between two layers.
# Multiple entries are independent โ€” they don't compose.
[[violations]]
from = "core"
to   = "data"

[[violations]]
from = "core"
to   = "feature"

[[violations]]
from = "data"
to   = "feature"

Missing sections are silently treated as "skip this analysis" โ€” e.g. an absent [visibility] skips the visibility audit, an empty [[violations]] table means no violation rules are active. A missing modulith.toml is a hard error with a "run modulith-android init" hint.


๐Ÿ“‹ Requirements

Tool Why Notes
Python 3.10+ runs the analyzer stdlib-only on 3.11+; one backport (tomli) on 3.10
uv or pipx (recommended) one-shot or isolated install pip install --user modulith-android works too
Gradle project the analysis target Groovy or Kotlin DSL settings.gradle โ€” both are supported

The analyzer itself has zero runtime dependencies on Python 3.11+ and exactly one (tomli) on Python 3.10. No HTML/JS framework, no JSON schema library, no graph library โ€” pure stdlib. No third-party Gradle plugin in the consumer's project either: every feature works against a vanilla Gradle build.


๐Ÿงฉ How it works

                       modulith-android
                              โ”‚
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ 1. read modulith.toml โ€” every project-specific value           โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
   โ”‚ 2. parse settings.gradle[.kts]            โ†’ list of modules    โ”‚
   โ”‚    parse each module's build.gradle[.kts] โ†’ edges              โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
   โ”‚ 3. core graph algorithms (stdlib only):                        โ”‚
   โ”‚      Tarjan SCC          โ†’ cycles                              โ”‚
   โ”‚      layer-pair filter   โ†’ violations                          โ”‚
   โ”‚      reverse-BFS         โ†’ cascades                            โ”‚
   โ”‚      fan-in/out, etc.    โ†’ metrics                             โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
   โ”‚ 4. cross-check declared project deps against source imports    โ”‚
   โ”‚      โ†’ unused module dependencies                              โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
   โ”‚ 5. scan public API of audited modules; classify each symbol    โ”‚
   โ”‚      by external-consumer count + Hilt/Koin DI markers         โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
   โ”‚ 6. render a single self-contained HTML file                    โ”‚
   โ”‚      โ†’ build/dependency-graph/index.html                       โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Every output is deterministic: given identical inputs the HTML is byte-for-byte the same, so regenerating after a code change produces a clean diff you can review.


๐Ÿ—‚๏ธ Project layout

modulith-android/
โ”œโ”€โ”€ src/modulith_android/
โ”‚   โ”œโ”€โ”€ cli.py                 entry-point; argparse dispatch
โ”‚   โ”œโ”€โ”€ config.py              ModulithConfig dataclass + TOML loader
โ”‚   โ”œโ”€โ”€ wizard.py              interactive `init` flow with auto-detect
โ”‚   โ”œโ”€โ”€ parsers/
โ”‚   โ”‚   โ””โ”€โ”€ gradle_modules.py  settings.gradle[.kts] + build.gradle[.kts] reader
โ”‚   โ”œโ”€โ”€ analyzers/
โ”‚   โ”‚   โ”œโ”€โ”€ unused_module_deps.py  cross-checks declared project deps against source imports
โ”‚   โ”‚   โ””โ”€โ”€ visibility_audit.py    public-API scanner + DI-marker classifier
โ”‚   โ”œโ”€โ”€ core/
โ”‚   โ”‚   โ”œโ”€โ”€ model.py           Edge, Module, GraphSnapshot
โ”‚   โ”‚   โ”œโ”€โ”€ cycles.py          Tarjan SCC
โ”‚   โ”‚   โ”œโ”€โ”€ cascades.py        transitive impact computation
โ”‚   โ”‚   โ”œโ”€โ”€ metrics.py         fan-in, fan-out, instability, abstractness
โ”‚   โ”‚   โ””โ”€โ”€ violations.py      layer-rule checker
โ”‚   โ”œโ”€โ”€ reports/
โ”‚   โ”‚   โ”œโ”€โ”€ html.py            the self-contained HTML+CSS+JS report
โ”‚   โ”‚   โ””โ”€โ”€ json_export.py     machine-readable graph dump
โ”‚   โ”œโ”€โ”€ common/
โ”‚   โ”‚   โ””โ”€โ”€ paths.py           module-path utilities
โ”‚   โ””โ”€โ”€ flows/
โ”‚       โ””โ”€โ”€ analyze.py         orchestrates the full pipeline
โ”œโ”€โ”€ tests/                     stdlib-unittest suite, no pip deps
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ LICENSE
โ””โ”€โ”€ README.md

๐Ÿง‘โ€๐Ÿ’ป Direct CLI

uvx/pipx are the easy paths, but the package is a normal CLI. Once installed:

modulith-android [SUBCOMMAND] [OPTIONS]

Subcommands:
  init                run the wizard; writes ./modulith.toml

Options (default run):
  --repo PATH         repository root (default: cwd)
  --out PATH          output directory (default: <repo>/build/dependency-graph)
  --config PATH       config file (default: <repo>/modulith.toml)
  --quiet             suppress per-phase progress logging on stderr
  -h, --help          show this help and exit
  --version           show version and exit

Exit codes:
  0  success; HTML report written
  1  configuration or I/O error

๐Ÿงช Development

pip install -e ".[dev]"
python -m pytest tests/                          # full suite
python -m pytest tests/test_visibility_audit.py  # one module
ruff check src/ tests/                           # lint

Tests use stdlib unittest; pytest discovers them with no configuration. CI runs the suite on Python 3.10 and 3.12.


๐Ÿ—บ๏ธ Roadmap

  • v0.1.0 โ€” analyzer core, modulith.toml config, wizard with auto-detect, self-contained HTML report, PyPI publication.
  • v0.2.0 โ€” JSON export improvements; richer cascade visualisation; configurable Hilt/Koin marker sets.
  • v0.3.0 โ€” multi-DSL improvements (KTS multi-line includes, type-safe project accessors).
  • v1.0.0 โ€” API stability promise.

๐ŸŽฏ Scope

modulith-android focuses on modularity: graph shape, layer rules, visibility, and unused inter-module dependencies. It does NOT audit external Maven dependencies or Gradle configuration hygiene (api vs implementation, scope choices). For that, plugins like the Dependency Analysis Gradle Plugin are excellent companions and orthogonal to what this tool does.


๐Ÿ“„ License

MIT โ€” see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

modulith_android-0.1.2.tar.gz (59.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

modulith_android-0.1.2-py3-none-any.whl (51.0 kB view details)

Uploaded Python 3

File details

Details for the file modulith_android-0.1.2.tar.gz.

File metadata

  • Download URL: modulith_android-0.1.2.tar.gz
  • Upload date:
  • Size: 59.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for modulith_android-0.1.2.tar.gz
Algorithm Hash digest
SHA256 3e4f3122d3314dcd5e999dabf008a09075f133beb082510941dd8af3ffbe1127
MD5 d1655a24650b092591d372dcea9a953d
BLAKE2b-256 1e141beefdb4aa3f739743c68c377fee31f7a53f7c4f6dc0694a83058bce0c5b

See more details on using hashes here.

File details

Details for the file modulith_android-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for modulith_android-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d3cedae527551b741fe309e5cdb13ecd1dd1bd00fa05ac789b134f2fd06f1913
MD5 36f789b785b36a18b87581a8f29e29a3
BLAKE2b-256 e16e3fb6a4f5301050ed471fff5016c762047dd06fca5379ca4c1f496708b650

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page