And*_*use 18 database-design design-patterns join
我正在寻找我的应用程序的模型层的编程/设计模式,并且我想知道哪一个最适合您正在进行涉及跨多个表的连接的检索的情况.
例如,假设您有以下表/关系:客户 - > 1..n帐户 - > 0..n功能
功能可以是支票簿,或免费旅行保险等优质产品.
然后我想做getCustomersForFeature()来检索所有拥有免费旅行保险账户的客户.
使用ActiveRecord或数据访问对象似乎不适合,因为这些通常关注每个表的一个类; 同样适用于Data Mapper.我意识到我可以将其分解为每个表的操作,例如getAccountsForFeature()和getCustomersForAccount(),但我想在一次点击中进行检索.
如果我们要"弯曲"每个表的一类模式并使用数据访问对象模式,比如说,getCustomersForFeature()方法是否会继续使用CustomerDAO或FeatureDAO?但这对我来说并不合适,因为你会在知道其他表的情况下污染你的DAO.
建议请.
在模型驱动设计中,您的应用程序中有逻辑实体,这些实体可以映射到物理数据库中的多个表.当然,您需要运行更复杂的SQL来获取完整的实体,而Model类是实现这些关系的地方.
我认为ActiveRecord及其同类产品可以用于针对单个表的简单查询,但是试图强制将这些模式用于复杂查询是非常困难的.幸运的是,我们已经有了一种简洁的,特定于域的语言,您可以使用它来指定复杂的查询:SQL.
因此,在Model类中,您可以使用方法来执行逻辑应用程序级任务,例如getCustomersForFeature()
.在该方法的代码中,您应该使用ActiveRecord方法或直接SQL(如果需要)编写特定查询.因此,数据库的具体设计封装在Model类的一个位置.
这意味着我们需要打破Model和Table之间的耦合.Model和ActiveRecord类之间的OO关系不是IS-A - 它是HAS-A(或者有很多).
你的评论:那么我们的模特是什么?如果您的应用程序主要需要作为一个实体与客户合作,并将功能视为或多或少是客户的属性,那么您的模型将是客户,并且它将隐藏功能存储在数据库中的单独表中的事实.Customers模型将在内部使用ActiveRecord或纯SQL来收集所需的数据,以提供具有关联的多值属性的客户的复杂对象的完整视图.
但是,如果您的应用程序还需要直接使用功能,该怎么办?例如,管理员的屏幕,您可以在其中根据功能获取报告或创建新功能.然后通过Customer模型访问功能将是笨拙的.所以你毕竟需要一个功能模型.只有它有不同的方法来实现您需要使用功能执行的操作.
每个模型类都应该公开一个API,它只包含您需要对该模型执行的操作.甚至不需要对称.仅仅因为您的客户模型可以获取具有给定功能的所有客户,并不一定意味着您的功能模型需要获取给定客户的所有功能.遵循YAGNI规则.
但是,在创建了Customer模型和Features模型之后,这是否会导致重复知道表之间关系的逻辑?是的,它可以.这是属于对象 - 关系阻抗不匹配范围的许多问题之一.
我花了更多时间阅读这个主题(包括 Bill 的回答中引用的领域驱动设计迷你书)。那本书中有一个聚合概念,它最接近地代表了我想要实现的目标;客户封装并控制对帐户和功能的访问。如果我们坚持使用领域驱动设计方法,我们可以使用存储库来控制客户的检索,在我看来,这是封装数据库结构知识的地方。
我还查看了我的企业应用程序架构模式副本,虽然 ActiveRecord 模式似乎旨在将类直接映射到数据库表,但如果您选择不遵循 DDD 的 Repository 方法,那么Data Mapper会适用于复合映射。
感谢大家的贡献。我已经投票赞成有助于得出这个结论的答案。由于这可能不是本次讨论的最后一点,我已将此答案标记为 Community Wiki。
在 C#/Linq to SQL 中,它会类似于以下内容。(我假设实际上有一个功能类型的查找表,这样您就有一个功能类型的标准列表,然后它们与帐户的关系是分开的,因此FeatureTypeId将是您的FK值,可以从下拉列表中选择清单什么的。)
// or whatever data type your keys are in
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId)
{
return from feature in dbContext.Features
where feature.FeatureTypeId == featureTypeId
select getCustomer(feature.Account.Customer.Id);
}
Run Code Online (Sandbox Code Playgroud)
在某种程度上,您的 DAO/BAO 必须了解对象之间的关系,因此这是祖父母关系这一事实不应该太可怕。
至于它在 BAO 结构中的位置,可能会以任何一种方式进行争论。我可能会把它放在客户身上,因为那是我最终想要达到的目标。
编辑:正如托比指出的,关系是双向的;再次在 Linq 中,您也可以采用其他方式:
// or whatever data type your keys are in
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId)
{
return from customer in dbContext.Customers
from account in customer.Accounts
from feature in account.Features
where feature.FeatureTypeId == featureTypeId
select getCustomer(customer.Id);
}
Run Code Online (Sandbox Code Playgroud)
任何其他 ORM 都应该具有非常相似的行为,尽管语法和结构会有所不同。