遵循整洁架构的Python应用:实体关联与数据查询困惑
首先,你的实体设计完全符合Clean Architecture的要求——实体聚焦业务规则,不携带持久化相关的ID字段,这个理解没有偏差。当前的问题出在持久化层(Repository实现)的关联数据加载方式,而非实体本身的设计。
下面是两种无需依赖FactoryRepository即可构建Event实体的可行方案:
1. 联表查询一次性加载关联数据
直接在EventRepository的实现中,通过SQL联表查询同时获取Event和对应的Factory数据,然后直接映射成实体对象,无需调用FactoryRepository。这样既减少了数据库调用次数,也让EventRepository完整承担起"获取完整Event业务实体"的职责。
以SQLAlchemy为例,实现代码大致如下:
def get_event_by_id(self, event_id: int) -> Event: # 联表查询Event和对应的Factory数据 query_result = self.db_session.query(EventModel, FactoryModel)\ .join(FactoryModel, EventModel.source_id == FactoryModel.id)\ .filter(EventModel.id == event_id)\ .first() if not query_result: raise ValueError(f"Event with ID {event_id} not found") event_model, factory_model = query_result # 直接构建Factory实体 factory = Factory( name=factory_model.name, latitude=factory_model.latitude, longitude=factory_model.longitude ) # 组装并返回Event实体 return Event( message=event_model.message, timestamp=event_model.timestamp, source=factory )
2. 延迟加载(按需加载Factory数据)
如果业务场景中并非每次获取Event都需要用到Factory的完整信息,可以用代理对象实现延迟加载:创建一个FactoryProxy类,它对外暴露和Factory一致的接口,但只有当访问Factory的属性时,才会去数据库查询真实的Factory数据。
这种方式下,EventRepository构建Event时直接使用FactoryProxy作为source,避免不必要的数据库调用,同时保持实体层的纯净性。示例代码:
class FactoryProxy(Factory): def __init__(self, db_session, factory_id: int): self.db_session = db_session self.factory_id = factory_id self._real_factory = None def _load_real_factory(self): if not self._real_factory: # 直接从数据库查询Factory数据,无需依赖FactoryRepository factory_model = self.db_session.query(FactoryModel)\ .filter(FactoryModel.id == self.factory_id)\ .first() self._real_factory = Factory( name=factory_model.name, latitude=factory_model.latitude, longitude=factory_model.longitude ) # 重写Factory的属性,触发延迟加载 @property def name(self) -> str: self._load_real_factory() return self._real_factory.name @property def latitude(self) -> float: self._load_real_factory() return self._real_factory.latitude @property def longitude(self) -> float: self._load_real_factory() return self._real_factory.longitude
然后在EventRepository中构建Event:
def get_event_by_id(self, event_id: int) -> Event: event_model = self.db_session.query(EventModel)\ .filter(EventModel.id == event_id)\ .first() if not event_model: raise ValueError(f"Event with ID {event_id} not found") # 使用FactoryProxy作为source factory_proxy = FactoryProxy(self.db_session, event_model.source_id) return Event( message=event_model.message, timestamp=event_model.timestamp, source=factory_proxy )
总结
你的实体设计没有问题,核心是调整Repository层的实现逻辑:要么通过联表查询一次性加载完整数据,要么用延迟加载按需获取关联数据,都能避免依赖FactoryRepository来构建Event,同时符合Clean Architecture的依赖规则。
内容的提问来源于stack exchange,提问作者Pollastre

