关于在高层次上构建系统的一些想法。
假设您有一个包含以下图层的系统:
服务层用于填充域模型中的对象图。为了避免耦合,域模型将不是持久性感知的,并且不会对任何数据访问层具有任何依赖性。
然而,使用这种方法,域模型中的一个对象如何能够调用其他对象而无法使用持久性加载它们,从而将所有内容耦合在一起 - 这是我试图避免的。
e.g。订单对象需要检查一个Inventory对象,显然需要告诉Inventory对象以某种方式加载,或以某种方式填充它。
有什么想法吗?
答案 0 :(得分:4)
您可以从服务层注入任何依赖项,包括填充的对象图。
我还要补充说,存储库可以是一个依赖项 - 如果你已经为存储库声明了一个接口,你可以编写代码而不需要添加任何耦合。
答案 1 :(得分:2)
这样做的一种方法是在数据层和域模型之间建立映射层。
查看映射,存储库和外观模式。
基本思想是,一方面是数据访问对象,另一方面是域对象。
答案 2 :(得分:1)
要解耦你必须:“编程到'接口',而不是'实现'。” (Gang of Four 1995:18)
以下是有关该主题的一些链接:
Google搜索“程序到界面,而不是实现”将产生许多有用的资源。
答案 3 :(得分:1)
您应该查看Martin Fowler的Repository和UnitOfWork模式以使用系统中的界面
答案 4 :(得分:1)
让域模型层定义您需要调用的方法的接口,以及需要由这些方法返回的对象的POCO。然后,数据层可以通过从数据存储中提取数据并将其映射到域模型POCO来实现这些接口。
任何需要特定数据访问服务的域级别类都可以通过构造函数参数依赖于接口。然后,您可以利用依赖注入框架来构建依赖关系图,并在需要的地方提供正确的接口实现。
答案 5 :(得分:1)
在编写大量代码之前,为了分离您可能想要问自己几个问题的所有内容:
域模型是否真正与DAL分开?是的,我是认真的,你应该考虑这一点,因为对于现有项目来说,实际换掉RDBMS以支持不同的RDBMS是非常罕见的。坦率地说,编写应用程序的语言比数据库本身更为常见。
这种分离究竟是什么让你买的?同样重要的是,你输了什么?关注点分离(SoC)是一个很好的术语,它被抛出相当多。然而,大多数人很少理解为什么他们开始关注分离。
我提出这些问题是因为应用程序通常可以从与底层数据模型更紧密的耦合中受益。没关系,由于代码生成的性质,大多数ORM几乎强制执行紧耦合。我已经看到很多可能的SoC项目在测试期间发生了崩溃,因为有人在表中添加了一个字段而DAL没有重新生成......这种目的无法实现,恕我直言......
另一个因素是业务逻辑应该存在于何处?毫无疑问,有充分的理由支持在实际的数据库中放入大量的BL。同时,有些情况下BL需要存在于您的域类中或非常接近您的域类。 BL以这种方式传播,你真的可以将这两个项目分开吗?即使那些讨厌将BL放入数据库的人也会依赖于使用身份密钥并让数据库强制执行参照完整性,这也是业务逻辑。
在不知情的情况下,我建议您考虑展平数据访问和域模型层。您可以转移到“提供者”或“工厂”类型的体系结构,其中服务层本身不关心底层访问,但工厂处理它。只是一些激进的思考。
答案 6 :(得分:1)
到目前为止,我已经看到应用程序可以很好地分为三层:演示文稿 - >逻辑 - >数据 - 和实体(或商务对象)。在逻辑层案例中,您可以使用某种模式,例如交易脚本或域模型我假设您最后使用此模式。域模型可以使用数据映射器与数据层进行交互并创建业务对象,但您也可以使用表模块模式。
Marttin的Fowler 企业应用程序架构模式书中描述了所有这些模式。我个人使用事务脚本,因为它比 Domanin模型简单。
答案 7 :(得分:1)
一种解决方案是让您的数据访问层子类化您的域实体(例如,使用Castle DynamicProxy)并将自身注入它返回的派生实例中。
这样,您的域实体类仍然是持久性无知的,而您的应用程序使用的实例仍然可以将数据库命中为延迟加载辅助数据。
话虽如此,这种方法通常需要make a few concessions到您的ORM架构,比如将某些方法标记为虚拟,添加其他不必要的默认构造函数等。
此外,它通常是不必要的 - 特别是对于没有繁重性能要求的业务线应用程序,您可以考虑急切地加载所有相关数据:只需将库存项目与订单一起提升。
答案 8 :(得分:1)
我觉得这与我之前的回答有很大不同,所以这是一个新的回答。
另一种方法是利用控制反转(IoC)的概念。构建数据访问层实现的接口。每个DAL方法都应该获取一个参数列表并返回一个数据表。
服务层将通过接口实例化DAL并将该引用传递给您的域模型。然后,域模型将使用接口方法对DAL进行自己的调用,并确定何时需要加载子对象或其他内容。
类似的东西:
interface IDBModel {
DataTable LoadUser(Int32 userId);
}
class MyDbModel : IDBModel {
DataTable LoadUser(Int32 userId) {
// make the appropriate DB calls here, return a data table
}
}
class User {
public User(IDBModel dbModel, Int32 userId) {
DataTable data = dbModel.LoadUser(userId);
// assign properties.. load any additional data as necessary
}
// You can do cool things like call User.Save()
// and have the object validate and save itself to the passed in
// datamodel. Makes for simpler coding.
}
class MyServiceLayer {
public User GetUser(Int32 userId) {
IDBModel model = new MyDbModel();
return new User(model, userId);
}
}
使用此机制,您实际上可以按需更换数据库模型。例如,如果您决定支持多个数据库,那么您可以拥有特定于特定数据库供应商工作方式的代码,并让服务层选择使用哪个数据库。
域对象本身负责加载自己的数据,您可以在域模型中保留任何必要的业务逻辑。另一点是,域模型没有直接依赖于数据层,这保留了您对业务逻辑的独立测试的模拟能力。
此外,DAL不了解域对象,因此您可以根据需要交换它们,甚至可以单独测试DAL。