Repository navigation
refactor: enforce DDD layering with import-linter - #27
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reviewer's GuideThis PR introduces import-linter contracts that enforce the app’s hexagonal layering, framework-independent domain, and acyclic dependencies, then wires the checks into local linting and CI; application behavior is unchanged. Flow diagram for import-linter enforcement in development and CIflowchart LR
code[App imports]
lint[lint-imports]
contracts[Import-linter contracts]
result[Pass or fail]
code --> lint
lint --> contracts
contracts --> result
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="justfile" line_range="34-35" />
<code_context>
lint:
@echo "Running linter..."
uv run ruff check --fix --exit-non-zero-on-fix
+ uv run lint-imports
# run formatter
</code_context>
<issue_to_address>
**nitpick (bug_risk):** `just lint` does not run `lint-imports` whenever Ruff applies an autofix, because the preceding `ruff check --fix --exit-non-zero-on-fix` command exits nonzero and stops the recipe before reaching this line.
**Triggers:** When the tree contains any Ruff issue that is automatically fixable.
**Suggested fix:** Run the import-linter command independently of Ruff's fix status, for example by separating the commands with a shell construct that preserves execution while still returning Ruff's failure status.
```suggestion
uv run ruff check --fix --exit-non-zero-on-fix || (ruff_status=$?; uv run lint-imports; exit $ruff_status)
uv run lint-imports
```
</issue_to_address>Sourcery assessment
Approved.
| uv run ruff check --fix --exit-non-zero-on-fix | ||
| uv run lint-imports |
There was a problem hiding this comment.
nitpick (bug_risk): just lint does not run lint-imports whenever Ruff applies an autofix, because the preceding ruff check --fix --exit-non-zero-on-fix command exits nonzero and stops the recipe before reaching this line.
Triggers: When the tree contains any Ruff issue that is automatically fixable.
Suggested fix: Run the import-linter command independently of Ruff's fix status, for example by separating the commands with a shell construct that preserves execution while still returning Ruff's failure status.
| uv run ruff check --fix --exit-non-zero-on-fix | |
| uv run lint-imports | |
| uv run ruff check --fix --exit-non-zero-on-fix || (ruff_status=$?; uv run lint-imports; exit $ruff_status) | |
| uv run lint-imports |
Summary
Enforces the hexagonal layering (
main → graphql | adapters → logging_config → settings → domain) with import-linter, mirroring TeKrop/overfast-api#459. The current code already satisfies every contract: no app code changes.[tool.importlinter](pyproject.toml):layers(exhaustive):graphqlandadaptersare independent siblings, so onlymainimports the concrete adapter;domainsits at the bottom and imports nothing else fromapp.domain-pure:app.domaincan't import strawberry, starlette, httpx2, cachetools or pydantic_settings.acyclic: no import cycles between sibling modules.composition-root(covered bylayers),adapters-independent(single adapter),services-independent(no services),domain-layers(3-file domain).import-linter==2.15.*dev dependency;lint-importsruns injust lintand CI.CLAUDE.md,README.md) updated to match.Test plan
uv run lint-imports: 3 kept, 0 brokenruff check,ty checkjust test: 54 passed🤖 Generated with Claude Code
Summary by Sourcery
Enforce the application’s hexagonal dependency boundaries with import-linter and include the checks in local linting and CI.
Enhancements:
Build:
CI:
Documentation: