需要一些关于ASP.NET MVC 3架构的指导。

10

我是一名有经验的.NET开发人员,主要使用Webforms进行开发。我熟悉MVC,但还没有在商业项目中使用过。目前我正在进行一些自我教育,并对架构方面存在的差异感到有些困惑。请注意,在这个问题之前,我了解到并不存在正确或错误的答案,我只是在寻求一种优雅的解决方案。

首先,我想说的是,我不使用实体框架或任何类型的ORM - 我想直接实现自己的业务对象和数据访问代码(使用ADO、SPROCS等)以确保它们是最优的,这是我的个人喜好。这就是我在寻找一致信息时遇到困难的地方,因为大部分信息似乎都与LINQ to SQL或实体框架有关。

我的应用程序结构如下:

  • Web(MVC 3 Web应用程序)
  • Models(类库)

只有两个项目,因为我遇到了耦合问题,这也是我提出这个问题的根源。 我的模型类库包含以下内容...

  • 经典的业务对象,包含字段和属性
  • 每个业务对象的存储库类包含数据访问代码(使用ADO.NET直接SqlDataReaders等)
  • 每个存储库类的接口

我的问题是所有这些层之间的依赖关系,感觉不太对劲!

1.业务对象应该包含实现业务逻辑的方法,所以除了字段和属性外,还有实现任何所需逻辑的方法吗?

2.存储库类执行数据访问代码但知道业务对象,这也不太对劲,数据访问代码应该位于其自己的类库中,并且不知道有关对象的任何信息。

3.控制器(在Web层中)利用存储库接口,但为什么呢?它们不应该包含业务逻辑,应该由“模型”或业务对象处理?控制器肯定不应包含业务逻辑,因此这也不对。我不希望将业务逻辑放在存储库中,因为它们正在访问数据库。

我正在苦苦寻找一种优雅的应用程序架构,只是一个实现自己的对象、自己的数据访问代码并确保应用程序松耦合的基本轮廓。是否有人可以给我提供任何指导呢?

4个回答

6
我建议对您的解决方案结构做一些改变。
例如,项目可以按以下方式进行结构化:
核心库
这将是代表您的领域数据和服务接口的简单 POCO。这里没有业务逻辑,只有简单的属性。
例如: public class User { public int UserId { get; set; } public string Name { get; set; } }
- Core
      \Entities <-- Poco's
      \Services <-- Interfaces for services

服务

这里是包含您业务逻辑的地方。用户如何验证?我们如何计算订单应该自动存档或其他什么内容?通常,在这里我们不会做任何数据库操作。

仓储

这是您进行基本数据库操作的地方。您说您没有使用L2S或EF等。没问题。我强烈建议您使用Micro-ORM,例如DapperMassive

测试

您正在进行单元测试和集成测试,对吗?我推荐使用xUnit或nUnit作为测试框架。

Web应用程序

这是MVC3应用程序。我建议使用StructureMap进行依赖注入。(您在使用IoC / DI,对吗?)

您可能一开始会认为这很麻烦,对吧?但是,将其拆分为仅两个项目非常容易。

  1. Web应用程序
  2. 测试项目

而核心库、服务和存储库都存在于Web应用程序项目的文件夹中。

尽量不要过度设计Visual Studio解决方案。大多数人认为他们需要很多项目和抽象层..因为他们认为其他人也这样做,并且这是正确的事情。好吧,这种思维方式非常早在2000年代就已经过时了...而且到2012年 - 很多东西都发生了变化。

尝试使用DI / IoJ,NuGet将这些库下载到您的解决方案中,Micro-OR / M用于数据访问以及测试项目。

以下是一些要检查的项目,关于布局方式:

  1. JabbR.net
  2. RavenOverflow

太好了,谢谢,但是伙计们别再提ORM了!性能啊!性能! - David Abraham
@DavidAbraham "ORM的性能不佳"是同一枚硬币的两面。ORM有其优点和缺点。它们的设计目的是解决对象与关系型数据库之间的不匹配问题,这方面做得很好。它们并非万能药,但在它们所擅长的领域表现出色。 - Jason Meckley
微ORM应该被重新标记为微数据访问框架,因为它们对我来说看起来不太像ORM :) - Pure.Krome
没错。就像我们可以编写自己的MVC框架、IoC框架等等,你懂的。但是嘿!无论你喜欢什么,我只是想帮忙…… - Pure.Krome

1
  1. 也许,如果模型需要的话。大多数模型都是简单的DTO,具有公共属性。但您也可以轻松地封装其中一些内容。

  2. 不完全正确。存储库是业务对象的集合。它可以从数据库中创建这些对象的实例。

  3. 控制器是控制服务器上发生的事情的入口点。读取将从存储库加载数据到视图模型中,并将视图模型推送到视图中。写入将从存储库加载数据,更新域并保存更改。然后重定向到读取控制器操作。

最重要的是,不要过度思考架构。这种情况发生得太多了。开发人员想要清洁的代码,实际上使其更加复杂,而不是更简单易维护。

无论您是否使用数据库或ORM都无关紧要,您在存储库中具有相同的功能。


但是如果存储库根据定义知道业务对象,它们就是有依赖性的,这感觉不太对。它们应该返回数据块,然后“某个东西”应该构建对象,正是这个“某个东西”让我困惑。也许是服务层?但是那样我们真的会添加太多代码。 - David Abraham
我只是一名开发人员,通常不参与架构设计。但我发现我参与的大多数项目都采用了类似的设计,其中一个管理器是业务层中与存储库通信的“某物”。存储库仅返回实体或实体列表。管理器将这些实体映射到DTO并将它们发送回调用者。调用者很可能是由Web应用程序调用的服务。 - Nope
1
我不认为“存储库是业务对象的集合”这种说法正确。如果你指的是存储库模式,那么你应该“使用存储库将检索数据并将其映射到实体模型的逻辑与作用于模型的业务逻辑分离开来。”更多信息请参见:http://msdn.microsoft.com/en-us/library/ff649690.aspx - JasCav

1

我认为从小做起是好的,随着需求的增加,您可以开始扩大视野,并根据需要将工作分成不同层次。如果您只有2个模型、1个视图和一个要查询和更新的单个表,则为什么要将工作分成20多个项目呢?

从小做起,让“需求”推动设计。如果您需要在数据库中读写大量表格,现在可能是使用存储库模式的时候了。如果您发现现在需要至少10个或更多不同的模型,那么现在可能是实现包含DTO/模型并将其从Web应用程序中移出的库的时候了。

例如,如果您需要编写单元测试或进行TDD,您会发现立即能够模拟存储库、业务和服务层非常方便。

为了论证,我曾经见过类似以下结构的项目(但大多数项目都不需要这种程度的分离):

  • Web项目;消耗服务库和DTO库
  • 服务库;消耗业务层库和DTO库
  • 业务层库;消耗存储库、实体库和DTO库
  • 存储库;消耗实体库
  • 实体库
  • DTO库

大多数情况下,这些库都被进一步分解为不同的库,其中一个包含接口,另一个包含实现等等。我曾经参与过的一些项目中有超过50个库,以分离大部分甚至所有内容,但这总是基于需求或需要。

以上示例的想法是让Web应用程序仅处理将DTO传递给服务调用并从服务调用返回DTO。

您可以大多数时间使用DTO作为模型,但有时如果您需要基于几个DTO的平面模型,则可以创建自己的视图模型作为包装器来展开多个DTO。

服务调用一个位于业务层库中的管理器中的方法,传递一个DTO。该管理器将使用DTO并将其映射到一个或多个实体,并调用存储库上的方法传递这些实体。存储库返回实体,管理器使用它们构造DTO以发送回去。

有很多方法可以做到这一点,我相信没有一种方法是唯一正确的。

您可以使用依赖注入框架(例如Ninject)来实现进一步的分离和独立性。

以上仅是一个示例。它可能对某些人非常有效,而对另一些人则不可接受。这取决于许多因素,其中项目的规模是主要因素之一。

除此之外,无论如何,始终从一个可管理的架构开始,随着需要变化而不断重构,无论是为了满足单元测试中的模拟需求,还是因为模型数量已经超出了Web应用程序的可管理范围,或者现在有一个独立的团队必须编写服务层,而您不希望这会干扰您的开发,服务层现在可能必须被不同的UI层消耗,而不仅仅是您的Web前端等等...


0
一些要点:
  • 在你的存储库中拥有“业务逻辑”没有问题。事实上,大部分的“业务逻辑”都会在那里。你的存储库包含查询,如果这些查询有趣的话,那么这些查询将与你的“业务逻辑”密切相关。

  • 在你的控制器中拥有“业务逻辑”也没有问题。你的控制器控制着你的用户界面,而你的用户界面如何工作就是“业务逻辑”。

  • 按照定义,在分层架构中,每个层次都知道其上方层次的公共接口。你的数据访问可以看到你的领域层的公共接口。你的用户界面层可以看到你的服务层的公共接口。

  • 如果你的项目很小,可以手动编写自己的数据访问层,而不是使用ORM或类似工具,那么拥有完全解耦的星际远航架构可能只会让你的生活更加困难。(我是在暗示你写自己的数据访问层有点疯狂——至少试试像Dapper这样的轻量级框架)。

  • 根据你的需求做出选择。问自己“为什么X需要与Y解耦”。 答案可能是“这样我就可以在单元测试Y时模拟X”。 答案可能是“这样我就可以在我的其他项目中使用X,而不需要知道Y”。 如果你找不到答案,那么YAGNI。


仅作为一点提醒,无论是小项目还是大项目,我都会手写我的数据访问层(DAL)。我想在每个级别上控制性能。我不是疯了 :) 我创建T4文本模板,其中大部分代码都是由程序自动生成的,只需要进行一些微调。但是因为我编写了这些模板(针对所有类),所以我拥有了掌控权。 - David Abraham

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