领域驱动设计(DDD):领域与基础设施职责划分疑问
嗨,很高兴你在深入DDD时琢磨这些细节——这正是吃透DDD核心思想的关键!咱们一步步拆解你的问题:
你说得对,领域层的核心是纯业务逻辑和规则,必须脱离任何具体技术实现(比如Selenium、数据库驱动这些)。把Selenium这种网页自动化的技术细节塞进Page实体,会让它变成耦合了技术的“脏对象”,完全违背DDD中领域模型要聚焦业务的原则。
你的PageAutomator思路和Repository模式异曲同工:Repository把持久化这种技术操作从领域实体中剥离到基础设施层,PageAutomator则把网页检测的技术实现也移到这里,这完全符合DDD的分层思想。基础设施层本来就是用来承载各种技术适配、外部依赖实现的,Selenium属于典型的外部技术工具,放在这里再合适不过。
首先得明确:贫血模型的问题不是属性少,而是没有业务行为和规则。如果你的Page实体只存url和environment,完全没有业务相关的校验或逻辑,那确实会变成贫血的DTO,但咱们可以给它加业务属性和规则啊!
比如,Page可以负责验证自身的业务合法性:
class Page: def __init__(self, url: str, environment: Environment): self._validate_relative_url(url) self._validate_environment_access(environment) self.url = url self.environment = environment def _validate_relative_url(self, url: str): if not url.startswith("/"): raise ValueError("页面URL必须是以/开头的相对路径") def _validate_environment_access(self, environment: Environment): if environment.is_restricted and not self._has_access_permission(): raise PermissionError("当前环境禁止检测")
这样Page就有了自己的业务规则,不再是单纯的DTO,而是真正的领域实体。至于contains_phrase这种技术操作,本来就不属于业务规则,交给基础设施层的PageAutomator才是正确的分工。
应用服务层的角色是业务流程的协调者,它可以调用基础设施,但最好不要完全跳过领域实体。比如一个标准的用例流程应该是:
- 应用服务接收用户输入,构建
Page实体(这一步会触发实体的业务校验) - 调用基础设施层的
PageAutomator.does_page_contain_phrase(page, phrase)执行检测 - 把检测结果返回给用户
如果直接跳过领域实体,让应用服务直接调基础设施,短期可能省事儿,但长期业务规则变复杂时(比如要加检测权限、记录检测日志、多环境适配规则),你会被迫把这些业务逻辑塞进应用服务,导致它臃肿不堪,违背了应用服务要精简的原则。所以哪怕Page实体一开始功能不多,也建议保留它作为业务规则的载体。
哪怕你选择事务脚本这种更偏向过程式的模式,也应该把技术细节和业务逻辑分离。事务脚本里只负责描述业务流程(比如“校验用户权限→构建检测请求→执行检测→返回结果”),而Selenium的具体调用依然要封装到基础设施层的组件里。
这么做的好处是:以后如果要替换技术实现(比如把Selenium换成Playwright),只需要修改PageAutomator的代码,不用动业务流程的脚本;同时业务脚本也能保持干净,只关注业务逻辑,不被技术细节干扰。
最后给你几个小建议:
- 始终记住:领域层管“业务是什么”,基础设施层管“技术怎么实现”
- 不要为了避免贫血模型而硬加无意义的方法,只给领域实体加真正属于业务规则的逻辑
- 应用服务层尽量只做协调,不要承载业务规则或技术细节
内容的提问来源于stack exchange,提问作者Steven




