Android MVVM应用Domain层UseCase架构设计及Clean合规性疑问
直接在Domain层实现UseCase是否违反Clean架构?
首先明确结论:直接在Domain层实现UseCase完全符合Clean架构的核心规则,反而某些示例中拆分UseCase接口到Domain、实现到Data的做法,是对Clean架构的误解。
先理清Clean架构中各层的核心职责
Clean架构的核心是依赖规则:内层(Domain)不依赖外层(Data、Presentation),外层依赖内层的抽象。各层的核心分工是:
- Domain层:最内层,包含业务实体(Entities)和业务用例(Use Cases)——这部分是业务规则的核心,是整个应用最稳定的部分。Use Cases的本质是业务逻辑的执行流程封装,它定义了“为了完成某个业务目标,需要调用哪些业务规则、依赖哪些外部端口(比如Repository抽象)”。
- Interface Adapters层(对应你的Data层):负责将Domain层的抽象端口(比如Repository接口)适配到具体的外部实现(比如Room数据库、Retrofit接口),它依赖Domain层的抽象,而不是反过来。
为什么说“Domain层UseCase无需抽象”?
你提到的“为UseCase加抽象是过度设计”的判断,在绝大多数常规业务场景下是正确的,原因有两点:
- UseCase的业务逻辑是Domain层的核心:UseCase本身就是业务规则的具体执行载体,它的变化只来自业务需求的变更,而不是外部依赖的替换。外部依赖的替换(比如从网络数据源换成本地数据源),是通过依赖Domain层的Repository抽象来实现的——你只需要替换Data层的RepositoryImpl,UseCase的代码完全不需要改动。
- 抽象UseCase的适用场景极少:只有当同一个业务流程存在完全不同的业务逻辑实现时,才需要为UseCase定义抽象。比如“登录UseCase”有“密码登录”“验证码登录”两种完全不同的业务规则,这时候可以在Domain层定义
LoginUseCase接口,然后分别实现PasswordLoginUseCase和OtpLoginUseCase。但这种场景并不常见,大部分业务的UseCase逻辑是单一且稳定的,不需要额外抽象。
澄清对“Domain层仅含抽象”的误解
你提到的“Domain层应仅包含抽象,实现放Interface Adapters层”,是混淆了外部依赖端口和业务逻辑实现的区别:
- 属于“外部依赖端口”的组件(比如Repository),确实需要在Domain层定义抽象,实现放在Data层——因为Repository是Domain层需要依赖的外部能力,Domain不需要关心具体怎么实现,只需要定义需要什么能力。
- 但UseCase不属于“外部依赖端口”,它是Domain层自己的业务逻辑执行单元,本身就是Domain层的核心实现,自然应该放在Domain层。
推荐的包结构实践
结合你的Android MVVM场景,推荐的核心包结构应该是:
├── domain/ │ ├── entities/ # 业务实体(比如User、Order) │ ├── repositories/ # Repository接口(比如UserRepository) │ └── usecases/ # UseCase实现类(比如GetUserUseCase、UpdateUserUseCase) │ ├── GetUserUseCase.kt │ └── UpdateUserUseCase.kt ├── data/ │ ├── repositories/ # Repository实现类(比如UserRepositoryImpl) │ ├── datasources/ # 具体数据源(比如UserLocalDataSource、UserRemoteDataSource) │ └── models/ # 数据层模型(比如UserDto,用于和数据源交互) └── presentation/ ├── viewmodels/ # ViewModel,依赖Domain层的UseCase └── activities/ # Activity/Fragment
在这个结构中:
GetUserUseCase直接在Domain层实现,依赖UserRepository接口UserRepositoryImpl在Data层实现,依赖具体的数据源- ViewModel通过DI注入
GetUserUseCase,完全不依赖Data层,符合依赖规则
内容的提问来源于stack exchange,提问作者Kratos
相关产品推荐
相关产品推荐

