背景
我正在尝试创建一个简单的应用程序来真正理解 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(); // 在应用服务层将构造函数注入到这个类中, // SimpleOrder.ApplicationFacade OrderEntry oe = new OrderEntry(customerRepository, orderRepository, productRepository);
为了完成这种依赖注入,我创建了三个存储库接口:
-- ICustomerRepository -- IOrderRepository -- IProductRespository
一个典型的实现:
命名空间 SimpleOrder.Domain.Interfaces { 公共接口 ICustomerRepository { 客户 GetCustomerById(int customerId); 无效 SaveCustomer(客户客户); } }
** 请注意,SaveCustomer 引用了域层中定义的 Customer 模型类。这是其他存储库的典型特征。
但是我不确定它们应该在哪个项目/层中实现。我在一个解决方案中有 5 个项目:
SimpleOrder.ConsoleClient (presentation) -- 我想从这里注入域的具体实现作为应用程序
SimpleOrder.ApplicationFacade(应用程序服务) ——在域中编排低级方法的大块高级、粗粒度方法
SimpleOrder.Contracts—— 用于表示和应用程序服务之间通信的 DTO 类
SimpleOrder.Domain(domain/bll) ——域模型类Customer、Order、OrderItem、Product
SimpleOrder.Repository (dal) -- 实现存储库接口
这是我看到的选项:
选项 1:在 SimpleOrder.Contracts 中定义存储库接口 ...
PRO:这是我认为他们应该属于的地方,因为我创建它是为了在各个关注点/层之间共享合同。例如,此处定义了 DTO。
缺点:但是每个接口的方法签名都引用了域模型类。
这意味着我必须添加对 SimpleOrder.Domain 的引用,但是当在另一个项目中引用 SimpleOrder.Contracts 时,它必须随身携带 SimpleOrder.Domain。这感觉不对。
选项 2:与上述相同的场景,但我还在 SimpleOrder.Contracts 中为每个域模型类定义接口,以便我可以打破存储库接口与实际模型类的耦合。
例子:
命名空间 SimpleOrder.Domain.Interfaces { 公共接口 ICustomerRepository { ICustomer** GetCustomerById(int customerId); 无效 SaveCustomer(ICustomer 客户); } 公共接口 ICustomer { int 客户 ID { 获取;放; } 字符串名称 { 获取;放; } System.Collections.Generic.List Orders { get; } } }
影响:每个领域模型类都必须实现他的相关接口。IE,
公共类客户:SimpleOrder.Domain.Interfaces.ICustomer { 公共客户() { _orders = 新列表(); } 公共 int CustomerId { 获取;放; } 公共字符串名称 { 获取;放; } 私有列表_orders; 公共虚拟列表订单{ 得到{返回_orders; } } }
PRO:修复选项 1 的问题。
缺点:这会增加项目中文件的数量(以及感知的复杂性),因为每个域类现在都有一个关联的接口。
选项 3:在 SimpleOrder.Domain 中定义存储库接口
影响:为了在运行时从 SimpleOrder.ConsoleClient 将具体的存储库类注入应用程序服务层(SimpleOrder.ApplicationFacade 项目),SimpleOder.ConsoleClient 还需要对 SimpleOrder.Domain 的引用。
PRO:这也解决了选项 1
CON:我试图避免直接从表示层引用领域层,因为现在表示层可以对领域层了解太多。当我将来用 WPF 或 ASP.NET MVC 应用程序替换控制台应用程序时,我会冒第二个和后续表示层实现尝试调用模型而不是应用程序服务层中的方法的风险。(但是我确实在选项 4 中考虑了这一点。)
选项 4:将接口放在 SimpleOrder.Domain 中,然后从 SimpleOrder.ConsoleClient 引用 SimpleOrder.Domain。
PRO:修复了上述所有问题。
CON:这感觉不对,因为当我应该只提供对 SimpleOrder.ApplicationFacade 中更高级别的块状方法的访问时,我将提供从表示层直接访问域层中较低级别方法的访问。
问题 我已经尝试了每一个,但已经选择了选项 4,但是这让我对它产生了不好的印象。有更好的选择吗?我在正确的轨道上吗?