我想现在申请我了解DDD和我有点困惑依赖的领域模型中的流动。
我的问题是:
- 如果一个实体需要注意的工厂,仓库,目前域名服务?
- 如果一个库是在域意识到服务的?
这是困扰我的脑海里的另一件事情是如何看待的,以集合时我想补充和实体集合。
比方说,我正在开发一个简单的CMS。 在CMS我有一篇文章实体和标签集合包含标签实体。
现在,如果我想补充的关系有一个新的标签。 什么是更好的方式来做到这一点? (在PHP实施例)
$article->tags->add(TagEntity);
$articleRepository->save($article);
或者我可以与服务做到这一点。
$articleService->addTag($article, TagEntity);
你怎么看?
谢谢。
实体和值对象不应该依赖于除了对方任何东西。 这是最根本的所有的DDD的积木 。 它们代表了你的问题域的概念,因此应重点关注的问题。 通过使他们依赖于工厂,库和服务您作出这样的对焦模糊。 还有,通过具有在实体和值对象到服务的引用的另一个问题。 由于服务也具有域逻辑分析,你会受到诱惑,委托一些领域模型的责任,服务,最终可能导致贫血的域模型 。
工厂及库只是用于创建和实体的持久性助手。 大多数时候,他们只是在真正的问题域没有相似之处,所以有工厂和仓库来服务和实体的引用将根据域逻辑并没有什么意义。
关于您所提供的例子,这是我将如何实现它
$article->addTag($tag);
$articleRepository->save($article);
我不会给底层集合的直接访问,因为我可能希望Article
执行一些领域逻辑上(施加约束,验证) Tag
将其添加到集合之前。
避免这种情况
$articleService->addTag($article, $tag);
使用服务只执行那些不属于任何实体概念上操作。 首先,尝试它适合的实体,确保它不适合所有。 然后才使用服务吧。 这样,你不会有贫血的领域模型结束。
更新1
从埃里克·埃文斯的引述“领域驱动设计:应对复杂性软件的核心”一书:
服务应谨慎使用,不得剥夺实体和他们的所有行为值对象。
更新2
有人已经downvoted这个答案,我不知道为什么。 我只能怀疑的原因。 这可能是有关实体和服务的引用,也可能是关于示例代码。 嗯,因为它是基于我自己的经验我认为我不能做太多关于示例代码。 但是,我做了引用部分的一些更多的研究和这里是我想出了。
我不是谁认为,从实体引用服务,存储库和工厂是不是一个好主意唯一的一个。 我发现类似的问题在这里SO:
- 它是确定的实体访问资源库?
- DDD -域模型,服务和存储库之间的依赖条件
- DDD -即实体不能直接访问存储库的规则
- DDD实体利用服务
也有一些好文章的主题,尤其是这一个怎么不注入服务实体也提出了一个解决办法,如果你迫切需要从你的实体,名为调用服务的双重分派 。 下面是从移植到PHP的文章的例子:
interface MailService
{
public function send($sender, $recipient, $subject, $body);
}
class Message
{
//...
public function sendThrough(MailService $mailService)
{
$subject = $this->isReply ? 'Re: ' . $this->title : $this->title;
$mailService->send(
$this->sender,
$this->recipient,
$subject,
$this->getMessageBody($this->content)
);
}
}
所以,你可以看到你不必在一个参考MailService
你的内部服务Message
的实体,而不是它作为参数传递给实体的方法传递。 “同样的解决方案是通过这篇文章的作者提出:服务DDD ”在http://devlicio.us/在评论部分。
我也想看看什么埃里克·埃文斯说,这个在他的“领域驱动设计:抢断复杂性软件的核心”一书。 在简要搜查,我没有找到确切的答案,但我发现一个实体实际调用静态服务,即无需将其参考的例子。
public class BrokerageAccount {
String accountNumber;
String customerSocialSecurityNumber;
// Omit constructors, etc.
public Customer getCustomer() {
String sqlQuery =
"SELECT * FROM CUSTOMER WHERE" +
"SS_NUMBER = '" + customerSocialSecurityNumber + "'";
return QueryService.findSingleCustomerFor(sqlQuery);
}
public Set getInvestments() {
String sqlQuery =
"SELECT * FROM INVESTMENT WHERE" +
"BROKERAGE_ACCOUNT = '" + accountNumber + "'";
return QueryService.findInvestmentsFor(sqlQuery);
}
}
而下面的注意事项规定如下:
注:QueryService的,从数据库中获取行和创建对象的实用程序,简单解释的例子,但它一定不是一个真正的项目的一个很好的设计。
如果你看看我上面提到的DDDSample项目的源代码,你会看到实体不知道除了在对象的任何东西任何参考model
包,即实体和值对象。 顺便说一句,该项目DDDSample中所描述的“领域驱动设计:在软件的核心应对复杂性”的书,详细...
同样的,一个我想与大家分享更多的是对类似的讨论domaindrivendesign雅虎集团 。 这个消息从讨论中引用的参考库模型对象的主题Eric Evans的。
结论
总而言之,具有从实体到服务,存储库和工厂一提的是不好的。 这是最接受的观点。 尽管库和工厂是域层的市民,他们都没有问题域的一部分。 有时(例如,在DDD维基百科文章)的域名服务的概念被称为纯虚构这意味着类(服务)“并不代表问题领域的一个概念”。 我宁愿是指工厂及库为纯属捏造,因为Eric Evans的不说别的东西在他的关于服务的概念书:
但是,当一个操作实际上是一个重要的领域概念 ,服务形成了一个模型驱动设计的一个自然组成部分。 在模型中作为服务声明,而不是作为一个假冒的对象,实际上并不代表什么,独立操作不会误导任何人。
根据上面所说,有时呼吁从你的实体服务可能是想一个理智的事情。 然后,您可以使用双调度的办法,这样你就不必在你的实体类服务的参考。
当然,总有一些人谁与喜欢的作家的主流意见不同意访问域服务从实体文章。
如果一个实体需要注意的工厂,仓库,目前域名服务?
实体不应该引用库或应用服务。 它是可以接受的实体引用一个工厂,如果它使用它来创建组成实体。 这也是可以接受的一个实体对域名服务的依赖,如果它使用的服务,为某种行为。
如果一个库是在域意识到服务的?
一般无。 存储库应该只负责持久性。
现在,如果我想补充的关系有一个新的标签。 什么是更好的方式来做到这一点?
这取决于你是指哪一层。 在一个典型的DDD的架构,你必须代码的两件。 你必须它封装领域,并提供细粒度的方法,如文章的应用服务addTag
在那里你会传递一个文章的ID和标签ID。 此方法将获取适当的文章和标签实例(如果需要),则:
$article->tags->add(TagEntity);
$articleRepository->save($article);
根据该域中的所有外层将通过应用服务与它通信。
如果一个实体需要注意的工厂,仓库,目前域名服务?
- 应用服务: 无
- 域名服务: 是的 ,因为他们是在领域层
- 工厂: 是的 ,因为他们是在领域层
- 库接口: 是的 ,因为他们是在领域层
- 库实现: 没有 ,因为他们是在基础设施层
注意接口和实现之间的differente:这就是为什么你应该使用接口与实现。
而仅供参考,工厂及库毕竟是服务,这样你就可以概括:
简单
域服务是其中的一个领域层内限定,虽然实现可以是基础设施层的一部分。 库是一个域名服务,它的实现确实是在基础设施层,而工厂也是一个域服务,它的实现是一般领域层内。
(来源: http://www.methodsandtools.com/archive/archive.php?id=97p2 )
如果一个库是在域意识到服务的?
通常没有,为什么会毕竟? 该库管理持久性。 但我不认为这“禁止”,因为基础设施层(持久性)知道的领域层。
这是困扰我的脑海里的另一件事情是如何看待的,以集合时我想补充和实体集合。
如果可能更喜欢面向对象的方法:
$article = new Article();
$article->addTag($tag);
$articleRepository->save($article);
我做的方式更有意义。
域名服务是任何业务逻辑不容易活的实体内。
(http://www.methodsandtools.com/archive/archive.php?id=97p2)
或者也可以:
当域中的显著进程或转型不是一个实体或值对象的自然责任,增加操作的模型声明作为服务的独立接口。
(Eric Evans的)
综上所述,创建一个域服务,如果你觉得你需要,这不是自动的。
我会,我不认为这是一个正确的答案,只是不同方法的前提回答这个问题。
在域对象的角度来思考,我会说的第一种方法是DDD。 您正在使用的域对象纯粹的处理。
我认为不过有用于暴露以此为API /服务层一部分的服务对象使用。 在这种情况下,您为您服务#2之内从包裹#1你的代码。 这可以让你避免暴露你的域对象给了国外消费者 - 你可以保持外部接口/ API不变,同时更新您的域模型。
然而,这仅仅是一种意见。
在工程师通过创建不必要的服务类等更少的代码是很好的它很容易 - 第一件事,第一,没有得到挂起来就可以了。 检查出的原始资料 ,并且无论是在代码的Java或C# 。
1)如果一个实体需要注意的工厂,仓库,目前域名服务?
它可以根据需要。 例如带注释我(JAVA)类@Entity
也可以与被注释@Configurable
并有注入到它的会话/其它类。 这是问题所在 - 封装所有必要的业务逻辑和暴露位于一个领域类清晰简单的API。
2)如果一个库是在域意识到服务的?
不可以,但相反的是有可能的,一个服务将使用的存储库。
利用多于一个的域对象/实体/根骨料当服务被使用。 因此,假如一个标签是一个独立的实体,这将是罚款:
$articleService->addTag($article, TagEntity);
但是,如果标签是ANOT另一根总,你可以只是做
$article->tags->add(TagEntity);
而文章本身也保存(无需任何其他电话)具有里面注入了一库/刀。