同一模块内App/Foo/Bar架构层的依赖限制实现方法问询
Awesome question! It's totally possible to enforce those clean dependency boundaries within a single module without splitting into separate submodules or messing with Gradle dependencies. Here are a few practical, actionable approaches:
Start with basic language-level access controls to lay a groundwork for your rules. This won't cover every restriction on its own, but it's a great starting point:
For Java Projects
- Bar Layer: Make all Bar classes
publicso both App and Foo layers can access them. Since Bar shouldn't reference App/Foo types, any accidental imports will either fail to compile (if App/Foo types are non-public) or be caught by additional checks later. - Foo Layer: Expose only the classes/methods the App needs as
public. Keep internal Foo utilities as package-private (no access modifier) to block Bar from accessing them. Note: Public Foo classes will still be visible to Bar, so we'll need extra checks here. - App Layer: Mark all App-specific classes as package-private. This instantly blocks Foo and Bar from accessing App layer code (since they're in separate packages), perfectly enforcing the "App → Foo/Bar only" rule in reverse.
For Kotlin Projects
- Bar Layer: Use Kotlin's default
publicvisibility for Bar classes so App/Foo can access them. Avoid referencing App/Foo types—if you mark App/Foo's internal types asinternal, Kotlin's compiler will throw errors if Bar tries to access them (thoughinternalis module-wide, so we'll pair this with other tools to lock it down). - Foo Layer: Use
publicfor classes the App needs, andinternalfor internal implementation details. - App Layer: Since Kotlin doesn't have package-private, mark App classes as
internaland use static analysis tools (see below) to restrict which packages can access them.
ArchUnit is a powerful static analysis tool that lets you define and enforce architectural rules directly in your code. It works for both Java and Kotlin, runs as part of your build, and catches any accidental cross-layer references.
Here's how to set it up:
- Add ArchUnit to your module's build dependencies (no separate modules needed—just add it to your existing Gradle/Maven config).
- Create a test class (e.g.,
ModuleArchitectureTest) to define your rules:@AnalyzeClasses(packages = "com.yourpackage.yourmodule") public class ModuleArchitectureTest { // Rule 1: Bar layer cannot access App or Foo layers @ArchTest public static final ArchRule bar_cannot_access_others = noClasses() .that().resideInAPackage("..bar..") .should().accessClassesThat().resideInAnyPackage("..app..", "..foo.."); // Rule 2: Foo layer can only access Bar (no App access) @ArchTest public static final ArchRule foo_only_accesses_bar = noClasses() .that().resideInAPackage("..foo..") .should().accessClassesThat().resideInAPackage("..app.."); // Optional: Explicitly document App's allowed dependencies @ArchTest public static final ArchRule app_can_access_foo_bar = classes() .that().resideInAPackage("..app..") .should().onlyAccessClassesThat().resideInAnyPackage("..app..", "..foo..", "..bar.."); } - Run the test—any violations (like Bar referencing a Foo class) will fail the build, strictly enforcing your dependency rules.
This is the most reliable way to lock down all your boundaries, as it catches edge cases that language modifiers might miss.
Pair the above with IDE settings to get immediate feedback while coding:
- IntelliJ IDEA/Android Studio: Use the Structure Search and Replace tool to create custom inspections that flag forbidden cross-package references. For example, set up an inspection that triggers when a class in
..bar..imports a class from..foo... - Use the built-in Dependency Analyzer (under
View > Tool Windows > Dependency Analyzer) to visualize cross-package dependencies and spot violations early.
内容的提问来源于stack exchange,提问作者Martin Mlostek

