Web应用程序架构建议

时间:2012-03-26 23:38:57

标签: c# asp.net-mvc model-view-controller architecture

我与之合作的团队(3人)目前正在讨论新的中型Web应用程序的架构,您可以想象团队内部有关如何设计应用程序的不同意见。我正在尝试做的是将这些想法中的一些与优势/劣势一起发布到我们所知道的每种方法中,以便SO社区可以帮助我们走上最佳路线和/或达成妥协。

架构A:

  1. 存储库层:

    致电实体框架

  2. 服务/业务层:

    • 每个“经理”都有一个界面
    • 每个'经理'有2个实现(1个模拟/ 1个实际)
    • Manager类中调用Repository然后应用业务逻辑的方法
    • 每个要进行单元测试的方法
  3. 表示层(MVC):

    • 通过某种服务定位器模式创建的经理
    • 每个控制器都要对其进行单元测试
  4. 架构B:

    1. 服务/业务层:

      • 仅包含具体的“经理”类
      • 方法直接调用实体框架(例如我们将EF视为回购)
      • 可以使用接口,但仅限于需要不同实现的地方
      • 在需要时进行单元测试
    2. 2。表示层(MVC):

      • 直接创建管理员课程
      • 单元测试仅脆弱或复杂程度较高的代码

      您可以做出一些假设:

      • 应用程序中的大多数方法都是数据库调用,根据用户权限/等返回奇数位的身份验证/限制数据
      • 95%的控制器没有明显的复杂性
      • 没有业务需要换出或具有不同的业务层类实现。如果我们不得不切换数据库/等,业务部门很乐意接受重构工作
      • 包括标记等在内的估计代码大小约为50k行(基于以前版本的Web应用程序,这是一团糟)
      • 开发时间紧迫
      • 有专门的QA人员

      很抱歉,如果这看起来相反,但我的首选架构实际上是B,原因有很多。

      1. 我已经完成了抽象到n度的项目,并且需要花费更长的时间才能对每个层进行更改,在VS中重建,修改测试/模拟方法/等等对于基本上简单的CRUD应用程序的项目而言回报不多

      2. 该公司并不关心该应用程序是否拥有世界上最干净的架构,他们只是想快速发货而不会有太多错误

      3. 对于绝大多数方法(在所有层中),只需要一个简单的功能测试。如果方法GetProductById不起作用,请修复它!在我看来,简单方法不需要包含比测试方法更多代码行的单元测试方法。

      4. 我希望能够右键单击某个方法,选择“转到定义”并查看该实际方法源!我不想看到接口定义,然后必须在别处查找实际方法。当然,如果所有东西都使用接口,那么交换到不同的数据库应该是轻而易举的,但实际上这不会发生,也不可能发生在这个项目中。

      5. 总而言之,我并不反对接口或单元测试,但是如果看起来有任何扩散,那么几乎没有任何好处,我无法理解这一点。

        任何人都有任何建议/建议吗?

3 个答案:

答案 0 :(得分:1)

架构A

  

1)存储库层:调用实体框架

提取OR / M是使依赖类(SL / BL)可测试的好方法。

  

2)服务/业务层: - 每个“经理”都有一个接口 - 每个“经理”有2个实现(1个模拟/ 1个实际) - 管理器类中的方法调用存储库然后应用业务逻辑 - 每个要进行单元测试的方法

不要将嘲笑放在业务层中。他们不属于那里。使用模拟框架,这些也允许您验证类在依赖项中调用正确的方法。

  

3)表示层(MVC): - 通过某种服务定位器模式创建的管理器 - 每个控制器都有针对它编写的单元测试

服务地点隐藏了依赖关系。改为使用依赖注入(今天最简单的方法是使用反转控制容器)

架构B

  

1)服务/业务层: - 仅包含具体的'Manager'类 - 方法直接调用实体框架(例如我们将EF视为repo) - 可以使用接口,但仅限于需要不同实现的地方 - 在需要时进行单元测试

不确定。你可以模拟EF4,但是要验证所有的调用是否正确都要困难得多。 Unit tests as and when when required。应始终如此。为什么要测试不需要测试的东西。公司政策告知何时以及要测试什么应该 始终

  

2)表示层(MVC): - 直接创建的管理器类 - 单元测试仅脆弱或复杂度很高的代码

直接创建类会使范围变得困难,您如何创建复杂的对象图?它邀请您使经理类成为上帝类或在类之间创建高度耦合。

<强>问题

  

1)我已经完成了抽象到n度的项目,并且需要花费更长的时间来对每个层进行更改,在VS中重建,修改测试/模拟方法/等等对于基本上简单的CRUD应用程序的项目,回报很多

很少有应用程序是CRUD。 Crud应用程序不会验证模型,而是检查语法是否正确。这些应用程序将包含大量从业务角度看无效的数据(但在格式正确时有效)

  

2)企业并不关心应用程序是否拥有世界上最干净的架构,他们只是希望快速发布产品而不会有太多错误

从现在起,维护成本一路飙升,他们确实关心了几年。

  

3)对于绝大多数方法(在所有层中),只需要一个简单的功能测试。如果方法GetProductById不起作用,请修复它!

从现在起六个月后,当您引入新的更改/功能时,您会做什么?强制用户测试 可能 会受到更改影响的所有内容?因为如果GetProductById在测试之前不起作用,你就可以了。

  

在我看来,简单方法不需要包含比被测试方法更多代码行的单元测试方法。

imho该方法需要重构并分解为更小的方法。难以测试的方法最有可能闻到。

  

4)我希望能够右键单击一个方法,选择“转到定义”并查看实际的方法源!我不想看到接口定义,然后必须在别处查找实际方法。

这个吓到了我。您是否愿意因为IDE无法帮助您而牺牲代码质量?购买resharper并获得“Goto实施”。 Resharper得到了很多软件质量检查,这非常物有所值。

  

当然,如果所有东西都使用接口,那么交换到不同的数据库应该是轻而易举的,但实际上只是没有发生,也不可能发生在这个项目中。

在任何项目中都不会发生这种情况,也不是使用接口的原因。定义良好的接口使得重构代码和遵循SOLID原则变得容易得多。

答案 1 :(得分:0)

根据业务需求,我似乎很清楚你应该坚持以最简单的方式编写代码,我会跳过所有TDD的东西并使用那个专门的QA人而不是跳过正式的接口声明,除非他们在有道理。

答案 2 :(得分:0)

我同意jgauffin所说的,但我想补充一些内容,所以我使用他的模板进行统一:

架构A

  

1)存储库层:调用实体框架

正如jgauffin所说,抽象O / RM是一个使BL可测试的好方法,实际上我会说这是对你进行单元测试的最好方法BL,这一般很重要。但请记住,advocates who say that you shouldn't hide the O/RM behind a repository有几个原因。从那篇博客文章:

  

这种模式的问题在于它完全忽略了成熟的持久性技术的存在,例如NHibernate。 NHibernate已经提供了内存访问的幻觉,事实上,这是其存在的唯一原因。声明性查询,检查。 OO查看持久性存储,检查。域和数据存储之间的单向依赖关系,请检查。

     

那么,当我已经拥有NHibernate(或类似的,大多数OR / M现在都具有匹配功能)时,通过使用存储库模式可以获得什么?

辩论仍然是开放的,所以这是你的号召。使用NHibernate,您仍然可以对BL模拟ISession进行单元测试,我不知道它如何与EF一起工作:或者您可以使用集成测试来测试您的BL,以及内存数据库(例如SQLite)。

  

2)服务/业务层: - 每个“经理”都有一个接口 - 每个“经理”有2个实现(1个模拟/ 1个实际) - 管理器类中的方法调用存储库然后应用业务逻辑 - 每个要进行单元测试的方法

jgauffin说你不应该把嘲讽放在你的BL中,他是对的,但是只要你的嘲笑是在一个额外的项目中,我看到在集成测试中只有优势,特别是如果你使用WCF服务:那么你可以对于模拟和实际服务具有不同的配置(或者根本不配置发现,只是范围),并针对多种配置测试您的服务。

  

3)表示层(MVC): - 通过某种服务定位器模式创建的管理器 - 每个控制器都有针对它编写的单元测试

有人声称Service Locator is an antipattern并且论证非常有说服力,即使有时服务定位器颂歌看起来像是较小的邪恶。与WebForms相比,MVC为依赖注入提供了更优雅的解决方案,所以你一定要仔细研究它。

架构B

  

1)服务/业务层: - 仅包含具体的'Manager'类 - 方法直接调用实体框架(例如我们将EF视为repo) - 可以使用接口,但仅限于需要不同实现的地方 - 在需要时进行单元测试

如前所述,如果它足够快,对于使用内存数据库的集成测试,这理论上可以很容易地工作。

  

2)表示层(MVC): - 直接创建的管理器类 - 单元测试仅脆弱或复杂度很高的代码

我可以看到不使用IoC容器的唯一优势是只有一个不太活跃的部分;出于测试目的,我仍然会使用DI,所以你真的想手动注入你的依赖项,穷人的DI风格,适合中型项目吗?

<强>摘要

为了保持简短,总有一个中间立场,我们无法真正为你做出决定。作为一个中等规模的项目,我倾向于类似于架构A;唯一的例外是,如果它是一个短期项目,你有一个确定的日期,你的应用程序已经过时并将被删除(想想一个特定的非经常性事件的应用程序)。

一般来说,如果它不是一个演示项目,我很少会跳过一个好的抽象。正如jgauffin暗示的那样,认为抽象不是交换实现的一种手段,而是作为管理应用程序复杂性的一种方式