在哪里为分层架构中的存储库定义接口?
Posted
技术标签:
【中文标题】在哪里为分层架构中的存储库定义接口?【英文标题】:Where to define the interfaces for a repository in an layered architecture? 【发布时间】:2012-11-30 12:54:34 【问题描述】:背景
我正在尝试创建一个简单的应用程序来真正了解 DDD+TDD+等的整个堆栈。我的目标是在运行时动态注入 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(演示文稿) -- 我想从这里作为应用程序注入域的具体实现
SimpleOrder.ApplicationFacade(应用程序服务) -- 在域中编排低级方法的大块高级、粗粒度方法
SimpleOrder.Contracts -- DTO 类用于表示和应用程序服务之间的通信
SimpleOrder.Domain(域/bll) -- 领域模型类 Customer、Order、OrderItem、Product
SimpleOrder.Repository (dal) -- 实现存储库接口
这是我看到的选项:
选项 1: 在 SimpleOrder.Contracts 中定义存储库接口 ...
PRO:这是我认为他们应该属于的地方,因为我创建它是为了在各个关注点/层之间共享合同。例如,这里定义了 DTO。
CON:但是每个接口的方法签名都引用了域模型类。 这意味着我必须添加对 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;影响:每个领域模型类都必须实现其相关接口。即,
公共类客户:SimpleOrder.Domain.Interfaces.ICustomer 公共客户() _orders = 新列表(); 公共 int CustomerId 获取;放; 公共字符串名称 获取;放; 私有列表_orders; 公共虚拟列表订单 得到返回_orders;专业版:修复选项 1 的问题。
CON:这会增加项目中的文件数量(以及感知的复杂性),因为 现在每个域类都有一个关联的接口。
选项 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。
专业版:修复以上所有问题。
CON:这感觉不对,因为我会从表示层提供访问权限 当我应该只提供时,直接到域层中的较低级别的方法 访问 SimpleOrder.ApplicationFacade 中更高级别的 chunky 方法。
问题 我已经尝试了其中的每一个,但已经选择了选项 4,但是这在我的嘴里留下了不好的味道。有更好的选择吗?我在正确的轨道上吗?
【问题讨论】:
仔细检查...选项 3 和 4 基本相同。哎呀。我应该更仔细地校对。 【参考方案1】:根据我对您问题的理解,我同意选项 4 是最好的。存储库接口应在所有域对象旁边的域层中声明。所述接口的实现应该是基础设施层的一部分——将你的领域层连接到世界的层。看看Hexagonal Architecture,看看这样做的一些动机。
要解决选项 4 的缺点,您不应将控制台应用程序视为仅仅是表示层。它还有其他职责,例如作为应用程序的主机和 DI 术语中的 composition root。控制台应用程序可能有一个表示组件,它只与应用程序服务通信。您还可以将应用程序服务封装在使用 ASP.NET WebAPI 实现的open host service 后面。然后表示层将只引用此服务,并对底层域层隐藏。
【讨论】:
根据你对我上一个问题和现在这个问题的回答,看来我要欠你一杯咖啡或牛排之类的了。 :) 我确实有一个快速跟进......你写了“根据我对你的问题的理解”......诚然,它是冗长的。是否有一些术语或思维过程听起来也有问题?再次感谢您的精彩回答。 我的意思是我不知道你项目的所有细节,所以我可能错过了一个约束。但是一个很好的问题仍然存在!【参考方案2】:我同意 eulerfx。我唯一要补充的是它感觉不对,因为传统的 N 层架构会让你让一切都依赖于数据库并使用抽象来打破这些依赖关系。
您应该查看Onion Architecture,这是您在选项 4 中组织依赖项的方式的一个示例。我个人认为这是可用的最具可扩展性的架构模型。如果您将此架构与 DDD 实践相结合,您会以最少的工作量获得最大的灵活性。
另一方面,我见过的许多实现将存储库和域服务的合同分解为自己的项目。不过,这纯粹是一个组织决定。将它们包含在您的域模型项目中是完全有效的。
【讨论】:
仅供参考,洋葱架构可以被认为与六边形基本相同;有些人认为这是抄袭,因为六边形在洋葱出现之前的一段时间就已经有了轮廓。 我明白你的意思了,谢谢你的信息。为什么两个架构模型都没有描述 UI/应用程序/基础架构之间的依赖关系?参考您的答案,应用程序有责任选择要使用的基础架构实现,这仅仅是因为这超出了他们试图用这些模型表达的范围,还是有其他原因将其排除在架构模型之外?我意识到您没有创建模型;我只是想知道您对此的看法。 在六边形中,UI只是另一个适配器,可以看作是基础设施。也许我误解了你的要求?您指的是 UI/App/Infrastructure 之间的什么依赖关系?我看到了一些关于这方面的观点。 通常,客户端应用程序和基础架构决策之间存在架构依赖关系。例如,移动应用程序可能使用 mysql,而 Windows 应用程序可能使用 SQL Express,而 Web 应用程序可能使用完整的 SQL。即使您通过 API 公开客户端界面(仅举一个应用程序不是 UI 的示例),它始终会推动后端实施决策。然而,这些架构模型都没有描述这种继承依赖关系,这会导致人们在尝试遵循特定模型时感到困惑。以上是关于在哪里为分层架构中的存储库定义接口?的主要内容,如果未能解决你的问题,请参考以下文章