背景
我正在尝试创建一个简单的应用程序,以真正理解DDD + TDD + etc的整个堆栈。我的目标是在运行时动态注入DAL存储库类。这让我
域和应用程序服务层可测试。我打算用“穷人的DI”来完成
现在...所以我将在启动附近的简单控制台应用程序中执行此操作:
//穷人的DI,在运行时注入DAL存储库类
var productRepository = new SimpleOrder.Repository.ProductRespository();
var customerRepository = new SimpleOrder.Repository.CustomerRepository();
var orderRepository = new SimpleOrder.Repository.OrderRepository();
//将构造函数注入到Application Services层的此类中,
// SimpleOrder.ApplicationFacade
OrderEntry oe =新的OrderEntry(customerRepository,orderRepository,productRepository);
为了完成这种依赖注入,我创建了三个存储库接口:
-ICustomerRepository
-IOrderRepository
-IProductRespository
典型的实现:
命名空间SimpleOrder.Domain.Interfaces
{
公共接口ICustomerRepository
{
客户GetCustomerById(int customerId);
无效SaveCustomer(客户客户);
}
}
**请注意,SaveCustomer引用了域层中定义的Customer模型类。这是其他存储库的典型特征。
但是我不确定应该在哪个项目/层中实施。解决方案中有5个项目:
SimpleOrder.ConsoleClient(演示)
-我想从此处将域的特定实现作为应用程序注入
SimpleOrder.ApplicationFacade(应用程序服务)
-块状的高级,粗粒度方法在领域中协调较低级别的方法
SimpleOrder.Contracts
-用于表示和应用程序服务之间通信的DTO类
SimpleOrder.Domain(域/ bll)
-域模型类Customer,Order,OrderItem,Product
SimpleOrder.Repository(dal)
-实现存储库接口
这是我看到的选项:
选项1:在SimpleOrder.Contracts中定义存储库接口...
PRO:这是我认为它们应属于的位置,因为我创建了此对象,以在各个关注点/层次之间共享合同。例如,此处定义了DTO。
缺点:但是,每个接口中的方法签名都引用域模型类。
这意味着我将不得不添加对SimpleOrder.Domain的引用,但是当
SimpleOrder.Contracts在另一个项目中引用,它将必须携带
顺着SimpleOrder.Domain。这感觉不对。
选项2:与上述情况相同,但我也为每个域模型定义接口
SimpleOrder.Contracts中的class类,这样我就可以打破存储库接口与实际模型类之间的耦合。
例:
命名空间SimpleOrder.Domain.Interfaces
{
公共接口ICustomerRepository
{
ICustomer ** GetCustomerById(int customerId);
无效SaveCustomer(ICustomer客户);
}
公共接口ICustomer
{
int CustomerId {get;组; }
字符串名称{get;组; }
System.Collections.Generic.List订单{get; }
}
}
影响:每个域模型类都必须实现其相关接口。即
公共类Customer:SimpleOrder.Domain.Interfaces.ICustomer
{
公开客户()
{
_orders = new List();
}
public int CustomerId {get;组; }
公共字符串名称{get;组; }
私人名单订单;
公共虚拟列表订单{
得到{return _orders; }
}
}
PRO:解决了选项1的问题。
缺点:这会使项目中的文件数量激增(并引起复杂性),因为
每个域类现在都有一个关联的接口。
选项3:在SimpleOrder.Domain中定义存储库接口
影响:为了在运行时从SimpleOrder.ConsoleClient将具体的存储库类注入到应用程序服务层(SimpleOrder.ApplicationFacade项目)中,SimpleOder.ConsoleClient还将需要引用SimpleOrder.Domain。
PRO:这也解决了选项1
缺点:我试图避免直接从表示层引用域层,因为现在表示层可以对域层了解太多。将来当我将来用WPF或ASP.NET MVC应用程序替换控制台应用程序时,我会冒着第二次和以后的表示层实现尝试尝试在模型而不是应用程序服务层中调用方法的风险。 (但是我确实在选项4中考虑了这一点。)
选项4:将接口放在SimpleOrder.Domain中,然后从SimpleOrder.ConsoleClient引用SimpleOrder.Domain。
PRO:修复以上所有问题。
缺点:这感觉不对,因为我将提供来自表示层的访问权限
当我只应提供时,直接使用域层中的较低层方法
访问SimpleOrder.ApplicationFacade中的更高级别的块方法。
题
我已经尝试了所有这些方法,但是都选择了方法4,但是在我的口中却留下了不好的味道。有更好的选择吗?我在正确的轨道上吗?
最佳答案
根据您对问题的了解,我同意选项4是最好的。存储库接口应在所有域对象旁边的域层中声明。所述接口的实现应该是基础结构层(将您的域层连接到世界的层)的一部分。看一下Hexagonal Architecture可以看到一些动机。
为了解决选项4的弊端,您不应将控制台应用程序仅视为表示层。它还具有其他职责,例如,作为应用程序的宿主和DI的composition root。控制台应用程序可能有一个表示组件,该组件仅与应用程序服务通信。您还可以将应用程序服务封装在使用ASP.NET WebAPI实现的open host service后面。然后,表示层将仅引用此服务,并从基础域层中隐藏。