我遇到以下情况的问题。我有一个文章实体:
class Article {
private $publishDate;
public function updatePublishDate(DateTime $date = null) {
$this->publishDate = $date;
}
}
我想应用一些业务规则来更新日期,例如: - 仅在尚未发布日期时更新日期 - 拒绝将日期设置为过去(必须是有效的发布日期)
因为在生成发布日期时有一些逻辑,我想为此设置单独的类,因为我知道它会改变:
class PublishService {
public function generatePublishDate() {
return new DateTime('tomorrow');
}
}
问题是:验证应该在哪里?我应该在实体中进行验证吗?
...
public function updatePublishDate(DateTime $date, PublishService $service) {
if ($date
&& $this->datePublish > new DateTime
&& $date >= $service->generatePublishDate()) {
$this->datePublish = $date;
} else {
throw new InvalidArgumentException('Something wrong with the date...');
}
}
...
或者我应该创建一个单独的ArticleService来处理这个逻辑吗?
我想到了什么:
答案 0 :(得分:0)
您定义的Article类旨在为已发布或未发布的文章建模(两种表示都使用相同的抽象)。 让我们稍微考虑一下方法" updatePublishDate"你为Article课写的。如果已发布的文章收到消息(方法调用)" updatePublishDate"?这很奇怪:一篇已经发布的文章在其协议中永远不应该有一条消息,这个消息为每个人打开,告诉它:#34; ey发表文章,将你的出版日期改为......"。所以这是一种气味设计。现在乍一看,我们可以看到我们需要一个代表发表一篇文章,另一个代表一篇正在撰写的文章。 因此,您将拥有两个带有回购的聚合:
DraftArticle > DraftArticleRepository
Article > ArticleRepository
每当您想要发表文章时,您都会选择一篇文章草稿,创建一篇文章并将其移至文章存储库:
Publisher.publishArticle(anId) {
draftArticle = draftArticleRepository.get(andId);
article = new Article(draftArticle.text());
articleRepository.add(article);
}
本文将当前系统日期作为发布日期。另一方面,条款草案不了解出版日期。但是,您可以在草稿文章中使用dateToBePublished,并且每天运行一个后台线程并发布符合时间条件的草稿文章。
希望它有所帮助。
答案 1 :(得分:-1)
" 用户可以在文章发布时提出,如果不是 已经"在线"。因为有一些文章规划,他有点 他的决定有限。所以PublishService首先计算 可能的发布日期。并拥有一个有效的实体(来自企业 逻辑观点)我需要检查用户提出的日期 大于或等于PublishService提供的日期。" - kodlik
嗯,首先,DDD就是在代码中反映域名。看看你如何向我描述用例(以粗体显示)以及它在代码中的实际反映方式:
public function updatePublishDate(DateTime $date, PublishService $service)
根据我上面的内容,我了解提议发布日期并不一定涉及立即发布文章。
proposePublishingDate
而不是updatePublishDate
呢?
现在,在方法级别将服务注入实体并没有什么不妥,但您应该遵循Interface Segregation Principle (ISP)并更明确地了解相关性。因此,您可以依赖于实现PublishService
方法的PublishingDateGenerator
接口,而不是依赖nextPublishDate
。
我希望您更多地关注原则,而不是我在这里选择的名称,但不要忘记这些是您的域模型的一部分,应该是您无处不在的语言的一部分。
从我所看到的情况来看,您似乎只是从PublishService
中提取日期,以便将其与用户建议的日期进行比较。
与其向PublishService
询问日期并使用日期来执行某些业务逻辑,为什么不通过PublishingDatePolicy
而使该规则成为您域名的明确概念?
以下是伪代码:
public proposePublishingDate(DateTime date, PublishingDatePolicy policy) {
if (this.isPublished()) throw ...;
if (!policy.isSatisfiedBy(date)) throw ...;
this.proposedPublishingDate = date;
}
注意:Evans一书中讨论了策略,这是明确规定业务规则的一种方法。它们与战略模式中的策略几乎相同。
您可能会将此视为违反Single Responsibility Principle (SRP),但我认为从与其密切相关的实体中提取简单逻辑并不总是值得的。但是,如果您希望规则经常更改,那么这可能是一个好主意......
很难说什么是最好的,每当你提出DDD问题时,你总能得到相同的答案:"这取决于你的域名。"。因此,您应该选择更好地与域对齐的方法,并且可以很容易地表达为无处不在的语言定义的域概念。