简单的应用程序架构建议

5
我正在尝试创建一个小型演示Web应用程序,它松散耦合,易于测试,代码清晰等。简而言之,我正在尝试以正确的方式做事。 ;)
目前,我的Wolfie解决方案中有三个项目:
- Wolfie.Core - 包含业务实体 - Wolfie.Data - 包含数据访问代码,引用Core。 - Wolfie.Web - 将成为Nancy网站。
目前为止,Core不知道其他任何项目的情况。Data必须引用Core,因为Core具有Data将返回的类型。因此,我意识到Web需要引用Core和Data才能工作,因为实体类型在Core中,数据库调用在Data中。
Data中的所有存储库类都有接口,以便可以对存储库进行模拟测试。
我不认为我想把任何特定于数据库的代码或引用放入Core中,并且我想把实体业务规则从Data中排除。
从Web引用这两个项目是正确的吗?还是需要另一个项目位于Web和其他项目之间,使Web仅引用一个位置并且不负责调用Data方法等。
我所追求的是一种架构,其中我的核心应用程序独立于Data和Web层。
希望我表达得清楚,期待您的有益回复。

听起来你已经得到了答案。你绝对正确地将业务核心逻辑与技术实现分离。这几乎听起来像是你熟悉 Jeffrey Palermo 在博客中描述的洋葱架构,并在他和其他人编写的 ASP.Net MVC In Action 书系中使用。 - StarTrekRedneck
5个回答

9
我认为你的目标是建立一个合理的应用程序架构,这是值得追求的。我个人曾经在一款企业Web应用程序上工作过,它使用了与你描述的类似的架构,并且对于该应用程序确实非常有效。
在那个应用程序中,域业务逻辑位于其自己的“Domain”程序集(你的“Core”程序集)中,并且没有引用.NET框架之外的任何内容。为了连接到外部业务组件(如数据库和其他Web服务),有一个“Infrastructure”程序集(你的“Data”程序集)。该应用程序的关键在于,Infrastructure程序集中的每个实现都是在Domain程序集中进行接口化的,并返回Domain程序集对象(就像你描述的那样)。当然,这需要从Infrastructure到Domain的引用。
为了将所有内容绑定在一起,一个“WebApp”程序集(你的“Web”程序集)引用了Domain和Infrastructure程序集,并使用IoC容器来解决所有应用程序依赖项。当请求发送到WebApp时,它会解析适当的“Domain”契约以服务请求,这将启动IoC解析调用链。WebApp程序集在注册Infrastructure实现之外,不关心Infrastructure程序集是否存在。
这种方法之所以适用于该应用程序,是因为它实现了你在问题中阐述的目标:
  • 在业务域中没有外部引用。
  • 业务领域应用程序完全独立于Web前端和数据库后端。
这使得你的业务域具有非常高的可测试性,以及与前端接口交互的极大灵活性。事实上,这种灵活性就是它如此易于测试的原因之一。这种灵活性还具有其他好处。例如,由于你的业务域被隔离开来,如果你想要在本地机器上部署和运行客户端,可以轻松地编写一个新的CLI或WPF前端,并连接到文件系统后端,而无需更改任何业务域代码。
虽然这个例子似乎有点牵强,但是对于我上面提到的那个应用程序而言,开发团队正在考虑构建一个Web服务前端,以补充已经存在的MVC网站前端。这仅仅是因为他们像你所描述的那样设置了该应用程序。

不错的故事!(如果您有服务层,可维护性将进一步提高;UI和Web /数据服务实现变得非常容易)。 - mikalai

4
我认为拥有这两个引用并没有本质上的问题。
但是,你需要考虑“Core”是什么。如果它是你的领域逻辑,那么我不确定你是否应该让数据访问程序集知道你的领域逻辑,并且你的GUI层也知道两者。
如果“Core”是领域逻辑,你可以在其中放置一些类(例如IRepository Pattern),这些类知道“Data”程序集。然后,你的Web程序集将知道“Core”,但不会知道“Data”。
为了促进这种安排,你可以创建第四个程序集称为“DataTransfer”,并定义一些对象(或更好的是接口),使“Core”、“Data”和“Web”都知道并用于彼此通信。事实上,如果你真的想要解耦,使用包含接口的“Types”程序集来构建结构,可以使你最初的三个程序集互相不知道。
但是,如果“Core”实际上是一个库而不是你的领域逻辑,那么按照你目前的结构可能是有意义的,如果你添加了足够的功能以开始添加更多的程序集/层,则稍后再重新审视它。

编辑:为了更好地说明我的意思,我谈论的是一种分层的情况:

Web
 | 
 v
Core
 |
 v
DataAccess

而不是:

Web
 |  \
 v   \
Core  |
 ^    |
 |    v
DataAccess

当你可以用两个参考来完成工作时,你却有了三个。过早的额外/不必要的耦合会在后期带来麻烦。


谢谢你的建议。所以,如果Core现在引用Data而不是相反,那么Data将返回/接收什么类型,因为它不知道Core中的类型?我是否需要某种中间类型(我猜这就是你所说的DataTransfer程序集)? - DavidGouge
2
将根业务逻辑(核心/领域等)引用任何技术实现(如数据库、文件系统等)视为错误。业务概念应该是独立的。数据访问层是业务概念的实现,例如IOrderRepository甚至IRetrieveOrderProcess。这就是为什么核心不应该引用DataAccess的原因。 - StarTrekRedneck
数据访问层(Data Access Layer)关注持久化机制的细节,它根本不是"业务概念的实现"。以下链接可能比我在500个评论字符中所能描述的更好地帮助您理解: http://msdn.microsoft.com/en-us/library/ee658127.aspx - Erik Dietrich
另外,为了澄清,我并不主张领域对象完全知道持久化对象。我主张领域内的对象(仓储)引用数据访问层接口,以获取数据传输对象并填充/创建丰富的领域对象。这些丰富的领域对象对应用程序的其他层一无所知,因此完全独立且可测试。 - Erik Dietrich
3
依我之见,他应该从一个WebApp->Core参考和一个WebApp->Infrastructure参考开始,其中包含需要外部依赖(不仅是数据访问)的业务概念的实现集合。IoC引导从WebApp的启动中调用。这是一个最小的解决方案,没有任何限制。如果他想将DataAccess分离到自己的程序集中,并处理DTO和映射,他可以轻松地这样做,并在基础设施程序集中处理映射,该程序集将引用该DataAccess层,但他不必这样做。Core将保持独立并非常可测试。 - StarTrekRedneck
显示剩余7条评论

3

你说的很有道理,推理非常合理。在Web层引用Core和Data是一种非常普遍、合理和实用的设计。你没有创建任何循环引用,保持得相当干净。


2
不,不是这样的。UI层不应该知道数据层的实现。然而,UI可以知道数据层抽象(以存储库接口的形式)。 - jgauffin

1

架构包括三个层次:1. 数据 2. 业务逻辑 3. 客户端。数据就是数据,业务逻辑对数据进行处理,客户端通过数据->业务逻辑->客户端的方式连接到业务逻辑。客户端不会直接连接到数据。这是一个三层结构,分别是数据、逻辑和客户端。


1

我认为这是有道理的。只要将Web层与业务层分开,就可以更轻松地测试业务逻辑,并通过其他方式(例如Web服务)公开业务层。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接