Skip to content

Reach data implementations through domain contracts - #77

Merged
AhmadKharfan merged 3 commits into
developfrom
refactor/domain-contracts-for-data-leaks
Jul 29, 2026
Merged

Reach data implementations through domain contracts#77
AhmadKharfan merged 3 commits into
developfrom
refactor/domain-contracts-for-data-leaks

Conversation

@AhmadKharfan

Copy link
Copy Markdown
Owner

Why

After the previous cleanup, three feature-to-data dependencies remained, and all three reached past :domain straight into concrete data-layer classes:

  • editor and buildrun constructed and called data.gradle.GradleProjectReader directly.
  • buildrun depended on data.remote.ActiveBuild / ActiveBuildRepository.
  • settings read the data.ai.AiProviderCatalog object.

These are the last main-source edges from a feature to a data implementation. Removing them means every feature now talks to :domain only, which is the precondition for enforcing that rule mechanically.

What changed

  • ActiveBuild and ActiveBuildRepository moved from :data:build to :domain. ActiveBuildStore and InMemoryActiveBuildStore stay in :data:build as implementations.
  • AiProviderDefinition and AiProviderCatalog moved from :data:ai to :domain. It is static reference data with no I/O, so it stays a plain object — wrapping it in an injectable interface would add indirection without buying anything.
  • New GradleProjectInspector contract in :domain, implemented by the existing GradleProjectReader.
  • gradleModule now also binds GradleProjectInspector to the same GradleProjectReader instance, so there is one parser rather than two.
  • editor, buildrun and settings build files updated: data:build is now testImplementation for both features, and settings drops data:ai entirely.

Why the Gradle contract is narrower than the class

GradleProjectReadResult exposes diagnostics, catalog and gradleProperties alongside the model — types owned by the Gradle parser. Moving it wholesale would have dragged GradleDiagnostic and VersionCatalog into :domain.

Tracing the call sites showed the features only ever use four things: isGradleProject, model, gradleVersion and agpVersion. GradleProjectSummary carries exactly those, and ProjectModel was already a domain type. The parser-specific types stay in :data:build, where read() still returns the full result for the data layer's own use.

Behavior

No behavior change. Types moved between modules and one interface was introduced; no logic was altered. GradleProjectReader.inspect() is a projection of the same read() call.

Scope

Both features keep testImplementation(projects.data.build) because their tests construct the real GradleProjectReader to parse fixture projects on disk. That is deliberate: main source depends on the abstraction, tests exercise the real implementation.

Not included: the data.local and data.buildsystem split packages, and the fact that :feature:buildrun still owns data.buildsystem.install.*. Both are handled in a later change.

Tests

No new tests. The existing suites plus two guards did the work:

  • KoinModuleGraphTest caught the missing GradleProjectInspector binding immediately — Koin's verify() failed with MissingKoinDefinitionException for EditorViewModel before anything reached a device.
  • Compilation is the check for the type moves: any missed reference fails the build.
  • 707 tests passing, 0 failures.

Verification

Verified from a clean worktree at the branch tip:

  • ./gradlew test detekt :app:assembleDebug — BUILD SUCCESSFUL
  • 707 tests, 0 failures

After this change no feature module has a main-source dependency on any data module. The only remaining projects.data.* entries across all seven features are three testImplementation lines.

Note on the detekt baselines: renaming the injected type from GradleProjectReader to GradleProjectInspector changed the constructor signature that keys two pre-existing LongParameterList baseline entries in :feature:editor and :feature:buildrun. Those entries were re-keyed to the new signature rather than regenerated, so no other finding is silently absorbed.

@AhmadKharfan
AhmadKharfan merged commit 42b8c50 into develop Jul 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant