Python DDD项目DI容器同一接口多实现注入方案咨询
落地实践方案
以下是工业界DI结合DDD项目的通用落地方式,按推荐优先级排序:
- 优先方案:按依赖角色拆分细粒度接口,零DI容器改造
你当前遇到的注入冲突本质不是DI容器能力不足,而是接口抽象粒度过粗:主存储数据源、缓存数据源虽然get方法签名一致,但二者在业务里的角色、职责、后续可扩展的能力完全不同,本来就不应该共用同一个接口。
直接将通用的DTODataSourceInterface拆分为两个语义明确的子接口即可:
仓储构造函数直接依赖这两个细分接口,DI容器可以分别绑定数据库实现、Redis实现,不存在匹配冲突。这种做法完全符合接口隔离原则与DDD的通用语言要求,后续给缓存加过期策略、给主库加事务逻辑时,两个接口可以独立演进,不会互相干扰。from abc import ABC, abstractmethod class PrimaryDataSourceInterface(ABC): @abstractmethod def get(id: int) -> DTO: ... class CacheDataSourceInterface(ABC): @abstractmethod def get(id: int) -> DTO: ... - 备选方案:给DI容器增加显式限定符(Qualifier)绑定能力
如果确实需要保留通用的DTODataSourceInterface抽象,不要用参数名匹配这类隐式规则(参数重命名、调整顺序就会触发注入错误,排查成本极高),可以参考成熟DI容器的实现,增加显式的限定符绑定机制:- 绑定阶段,给同接口的不同实现分配全局唯一的标识:
container.bind( interface=DTODataSourceInterface, implementation=DbDataSource(), qualifier="primary_db" ) container.bind( interface=DTODataSourceInterface, implementation=RedisDataSource(), qualifier="cache" ) - 注入阶段,在注入点显式声明需要的实现标识,Python 3.9+可以直接用原生
Annotated类型做标记,低版本可以用类装饰器、构造函数装饰器实现:from typing import Annotated class Repository(RepositoryInterface): def __init__( self, data_source: Annotated[DTODataSourceInterface, Qualifier("primary_db")], cache_data_source: Annotated[DTODataSourceInterface, Qualifier("cache")] ): self.data_source = data_source self.cache_data_source = cache_data_source
- 绑定阶段,给同接口的不同实现分配全局唯一的标识:
- 避坑提醒
不要为了省代码引入隐式匹配规则(按参数名、按参数位置、按实现类名模糊匹配),这类规则在项目规模小的时候看起来方便,随着代码量增长、参与开发的人变多,会出现大量只有运行时才能触发的隐蔽bug,排查成本远高于初期省下来的代码量。
内容的提问来源于stack exchange,提问作者Antonio Gamiz Delgado
相关产品推荐
相关产品推荐

