DDD“查看对象”?

Posted

技术标签:

【中文标题】DDD“查看对象”?【英文标题】:DDD "View Objects"? 【发布时间】:2010-12-21 23:55:38 【问题描述】:

给定一个涉及公司的应用程序,我可能有一个公司类。我将有一个填充列表 的数据访问层。但是,有时(例如显示搜索结果)我只需要显示公司名称、电话和邮政编码属性,在我看来,用所有属性填充整个 Company 对象似乎很浪费。

就 DDD 设计而言,解决此问题的正确方法是什么?我是否会创建 View 特定类,例如仅公开我有兴趣显示的属性的 CompanySearchResult 对象?

【问题讨论】:

【参考方案1】:

这听起来对我来说是一个合理的方法。

稍后,如果客户来找您,要求您的 SearchResult 显示与 Company 模型无关的东西 - 像附近冰淇淋店的数量这样疯狂的东西,您将更容易将其附加到您的 CompanySearchResult 比您的域对象。

【讨论】:

我在想一些更“疯狂”的事情,例如将搜索结果作为显示中的一个元素。我可能还想要一个网格中的日记提醒列表和当前天气摘要。我是否会将我的 CompanySearchResult、WeatherSummary 和 ReminderList 聚合到一个专门针对该表单或网页的 MyComplexFormViewModel 类中? @Michael - 在那种情况下,我可能会创建一个包含 CompanySearchResult、WeatherSummary 和 ReminderList 的容器视图 @Kirschstein - “容器视图”与我提出的 MyComplexFormViewModel 想法相同还是不同? @Michael - 我从您所说的内容中收集到的是,您正在考虑将模型 A、B 和 C 中的每个属性复制到您的 ComplexView。我会有一个包含 A、B 和 C 的视图。 @Kirschstein - 不,我认为 ComplexView 类将具有以下公共属性:CompanySearchResult(作为搜索结果对象的集合)、WeatherSummary 和 DiaryReminderList(作为 DiaryReminder 对象的集合)。跨度> 【参考方案2】:

这就是通常所说的“视图模型”或数据传输对象。您可能不希望您的视图有权访问您的域模型公开的整个数据树。尤其是如果公开你的领域模型意味着你的视图将不得不深入挖掘你的对象图来提取它需要的数据,那么视图模型对于简化模型对象的工作非常有意义。在您的情况下,如果您只是从模型对象中提取直接属性,那么如果您想隐藏域模型的其余部分不需要的无关数据,那将是有意义的。

【讨论】:

【参考方案3】:

您建议的方法可以快速增加您需要创建的 DAO 数量,并成为维护的噩梦。几个 ORM 采取的方法是代理数据访问,因此您的数据访问层将返回一个接口列表,并且数据库调用将被推迟到您调用数据访问器时,例如

list.getCompany(1).getName()

。这称为延迟加载。您仍然需要在进行许多小的或更少的大查询之间进行权衡。这种类型的任务是 ORM 的优势之一,您通常可以要求您的 ORM 预取您认为将使用的对象图的部分,而忽略其他部分。

【讨论】:

嗯。 ORM。对我来说,另一个探索途径...你能建议一个好的起点吗? @Michael - *** 上有大量与 .NET ORM 相关的问题,只需浏览一下即可。 NHibernate 通常最推荐用于大型项目,尽管它有一个陡峭的学习曲线(但越来越好)。对于较小的项目和“开始运行”类型的事务,如果您已经有数据库模式,可能是 Subsonic 或 LinqToSql。 我认为这不是一个好方法。您不应该盲目地使用“延迟加载”。您需要考虑在何时何地应用延迟加载,而不是像这样误用它。更多信息:davybrion.com/blog/2009/11/… 我更喜欢 'View' / SearchREsult 类。即使使用像 NHibernate 这样的 ORM,这也很容易使用/支持。您可以只查询您的域模型,并使用投影来获取“视图”对象。 误用?这就是发明延迟加载的原因 - 由于不同的用例,请延迟加载数据。为每个视图生成一个 DAO 实际上与将数据访问层集成到视图中相同,我不推荐这样做。那么你还不如直接通过一些标准 API 与数据源交互——比如 SQL?【参考方案4】:

我使用了实体属性的崩溃。例如:

// interface for "ID" attribute of Company entity
public interface ICompany_ID 
    Guid CompanyIDget;set;

// interface for "Name" attribute of Company entity
public interace ICompany_Name 
    string Nameget;set;

// interface for "Logo" attribute of Company entity
public interface ICompany_Logo 
    byte[] Logoget;set;


// interface for all attributes of Company entity
public interface ICompany : ICompany_ID, ICompany_Name, ICompany_Logo  

// base class for classes based on Company entity
public abstract class CompanyBase : ICompany_ID 
    // implementation of ICompany_ID interface


// class for all attributes of Company entity
public class Company : ICompany 
    // implementation of ICompany interface (all attributes)


// class for Company name lookup
public class CompanyNameLookup : CompanyBase, ICompany_Name 
   // implementation of ICompany_Name interfade

这个崩溃让我可以处理不同实体的不同属性,而且都是类型安全的。 但是,您的数据层必须支持这种情况。

下一种方法是动态创建查找类,但要复杂得多。另一方面,它更加灵活。

编辑: 那么选择可以是例如:

var companies = (from c in db.Table<ICompany>()
                 order by c.Name
                 select new CompanyNameLookup  ID = c.ID, Name = c.Name 
                ).ToList();

或者对于动态创建的类型:

var companies = (from c in db.Table<ICompany>()
                 order by c.Name
                 select DynamicTypeFactory.New<ICompany_ID>( c.Id ).And<ICompany_Name>( c.Name ).Create()
                ).ToList();

DynamicTypeFactory 是具有静态方法 New 的类和在运行时用于动态创建类的流畅接口。

【讨论】:

@Frederik:你认为为什么?我在几个成功的商业项目中使用了这个场景,效果很好。 我同意 Frederik tbh 的观点,它似乎过于复杂而收效甚微。

以上是关于DDD“查看对象”?的主要内容,如果未能解决你的问题,请参考以下文章

DDD实践_如何使用DDD设计代码模型

DDD 工厂实体值对象

用好 DDD 必须先过 Spring Data 这关

DDD 中的值对象 - 为啥是不可变的?

DDD简单入门

DDD~概念中的DDD(转)