我的困惑的根源是DTO与DDD和存储库.我想要我的POCO域对象拥有智能,我想从存储库获取它们.但是,似乎我必须违反一些封装规则来完成这项工作,而且似乎可以把DTO转到头上.
这是一个简单的例子:在我们的目录应用程序中,Part可以是包含许多其他部分的包.所以,Part POCO有一个’GetChildren()’方法返回IEnumerable&部分>.甚至可能在其出路上列出其他的东西.
但是该列表如何解决?似乎就是存储库的答案:
interface IPartRepository : IRepository<Part> { // Part LoadByID(int id); comes from IRepository<Part> IEnumerable<Part> GetChildren(Part part); }
和
class Part { ... public IEnumerable<Part> GetChildren() { // Might manipulate this list on the way out! return partRepository.GetChildren(this); } }
所以现在我的目录的消费者除了(正确地)从仓库中加载零件之外,还可以通过直接调用GetChildren(part)来绕过某些部件封装的逻辑.不是那么糟糕吗
我读到这个存储库应该提供POCO,但DTO对于在层之间传输数据是有好处的.计算了很多部件属性,例如,价格是根据复杂的定价规则计算的.价格甚至不会出现在存储库中的DTO中 – 所以看起来将定价数据传递回网络服务需要DTO消耗零件,而不是相反.
这已经太长了我的头在哪里松开?
例如,如果零件的价格取决于其零部件,价格可以在以下时间(至少)确定:
>建造时,如果父母部分(和所有其他必要的数据)可用.
>在AttachToParent(Part parentPart)方法中或响应一个事件,即OnAttachedToParent(Part parentPart).
>客户端代码需要的时候(即它的Price属性的首次访问).
编辑:我原来的答案(下)真的不是DDD的精神.它涉及到域对象简单的容器,许多被认为是反模式的设计(见Anemic Domain Model).
Part和IPartRepository之间的一个附加层(我将其称为IPartService)解决了这个问题:将GetChildren(part)移动到IPartService中,并将其从Part中移除,然后使客户端代码调用IPartService来获取Part对象及其子代,而不是击中存储库直接. Part类还有一个ChildParts(或Children)属性 – 它不知道如何自己填充它.
显然,这会增加额外的成本 – 如果在大多数情况下不需要额外的业务逻辑,您可能会最终编写或生成许多用于存储库调用的传递代码.