网页架构:MVC、延迟初始化、数据传输对象、在视图中打开会话,是否有共识方法?

StackOverflow https://stackoverflow.com/questions/1969278

对于典型的 Web 3 层应用程序,您认为以下设计存在哪些缺陷(以及您理想的架构建议是什么)?

我目前的蓝图方法非常粗略(假设Java,Spring,Hibernate,JSP)


控制器

无状态,可能用只读事务包装(以避免延迟初始化异常),仅通过服务从持久性存储中获取实体,将它们作为模型传递到视图。对它们进行业务逻辑(BL应该只在服务层吗?),如果需要的话传回服务层进行持久化。

优点:对于只读事务包装 - 只有一个连接,对同一持久实体没有冗余命中,更好地利用查询缓存,服务层不应该“知道”请求参数或所需的初始化图范围,避免惰性初始化异常。

缺点:只读事务方法可能存在风险,控制器不是理想的业务逻辑悬挂位置......JUnit 很难做(你的输入是一个请求......)


看法

非事务性(访问非惰性集合/成员将导致惰性初始化异常)

优点:

  • 视图作者不应仅通过点符号来影响应用程序的性能(例如由于延迟初始化大集合而导致 N+1 选择。

  • 此外,在断开连接的客户端(Flex 或其他富客户端)中,远程延迟初始化要么不被支持,要么就是不明智的做法

缺点:控制器/服务/DAO 必须仔细为视图准备正确的实体图,并且可能会超出(性能)/不足(延迟初始化异常)。无数的服务器端方法可能会导致混乱,因为实体图可以初始化的排列数量存在笛卡尔积


模型

按原样使用持久对象(无数据传输对象),状态保存在会话中。

优点:无需重写POJO,重用现有实体,会话状态比隐藏字段状态处理更安全。

缺点:对于断开连接的框架不利,保存过时的断开连接对象的风险,锁定问题的风险,覆盖其他数据的风险,有时需要乐观锁定。


服务

事务性,不知道请求范围,调用 DAO 层进行实际的持久存储访问。这是 BL 典型的位置,但 BL 似乎一遍又一遍地泄漏到控制器侧。


DAO

包含原子持久存储门面,不了解 BL 或任何上下文


最后,问题:

您将在上述架构中修复什么?

您是否认为(像我一样)这是一种非常常见的方法(有一些细微的差别,例如视图中的开放会话等)?或者这是你第一次看到它而我正在做一些非常错误(或正确)的事情?

您如何在应用程序中解决这个问题?您是否也将实体 POJO 用于模型和视图?或者您是否将其连接到更简单的 UI bean(全部完全初始化且安全)?

这可能是一个主观问题,但我确信有明确的最佳实践设计模式聚集到一个、两个或三个最大的一般“宗教”。

有帮助吗?

解决方案

总的来说,这似乎是一个非常好的架构。如果您还没有阅读过,我会推荐 Martin Fowlers 的企业应用程序架构模式,它描述了您问题中的每个主题。

从这个问题中尚不清楚您期望性能问题有多大。根据我的经验,性能瓶颈很少出现在您认为的地方,而且您越早发现它们,就越容易更改架构以匹配。

您说得对,可测试性是一个主要问题。我用过马丁·福勒斯 被动观点- 模式取得了一些成功。您还应该从同一站点查看 Supervising Controller。

其他提示

超级除非做一个SOFEA风格的前端,基本上摆脱了上面架构中的Controller部分。

前端完全包含在客户端上,直接调用返回 JSON 或 XML 的 REST 或 SOAP 服务。这似乎解决了 100% 转换域对象以进行显示的问题!!!!

有些人认为,上述 N 层架构之所以没有干净的解决方案,可能是因为它根本就是错误的。

链接

  1. http://raibledesigns.com/rd/entry/sofea_also_known_as_soui
  2. http://www.theserverside.com/news/thread.tss?thread_id=47213
  3. http://wisdomofganesh.blogspot.com/2007/10/life-above-service-tier.html

我当前的项目使用有点过时的 N 层架构和数据传输对象。DTO 是不必要的,因为应用程序不是分布式的,也永远不会分布式。使用 DTO 的一个好处(在我看来不值得)是它强制为业务方法提供一个干净的接口 - 视图组件可以按照自己的意愿遍历伪模型上的对象图 - 不会出现延迟初始化异常被扔掉。

我在我们的架构中看到的架构痛点之一是业务接口非常复杂。看起来我们需要一个元业务接口来封装所有的小业务接口。事实上,这种情况最终会发生在一个服务最终调用其他三个服务来完成其工作的情况下。

控制器(在我们的例子中是 Struts 1.2 Action 类)最终调用这些超细粒度或粗粒度业务组件的任意组合。遗憾的是,在大多数情况下,开发人员无意或懒惰地在控制器类中编写了各种本应是业务逻辑的代码。每次看到这三百行动作方法之一我就尖叫!!!!

SOFEA 方法似乎提供了一种更简洁的方法。AJAX 甚至允许在浏览器上运行的 Web 应用程序使用正确的 MVC 模式对其前端进行编码,这对用户(通过提供更动态的 UI)和开发人员(允许他们编码正确的 MVC)都有好处。

UI 与可重用的业务逻辑完全解耦。GUI是厚的还是薄的?这真的不重要——它们将以基本相同的方式编码。

SOFEA / SOUI 对我来说是新的,我从未尝试过,但我最近一直在阅读有关它的内容,我想我应该分享一下我的想法。

你的上述方法听起来不错。

但我认为你应该使用 UI-Beans。当然,这个 UI-Bean 实际上应该是不可变的。一旦它被创建,它的状态(以及封装的域对象)就不应该改变。

非常简单的例子:


class UIBean {
  DomainObject o;

  public String getDescription(){
     return trimToSummaryText(o.getDescription());
  }

  private static String trimForSummaryText(){
     ....
  }
}

主要优点:

  • 模板代码往往变得更加干净和简洁。您的前端开发人员会对此感到高兴。
  • 您不倾向于将特定于前端的帮助器方法添加到域对象类中。
  • 可以对不同的域对象或视图 bean 进行分组(ui-bean 可以有多个字段)。列表的封装在这里特别好。

是的,它涉及更多的 java 类。但只要你的 web 应用程序和页面增长,这个抽象层几乎总是没问题的。

尽管这个问题最后一次回答是在大约一年前,但也许这个答案似乎对某人有帮助。您的架构总体概述很好,唯一需要添加的细节是:通常 用于在层之间传递数据 (例如。View 和 Service) 而不是上面提到的 UiBean-s 所谓的 DTO 使用(数据传输对象),这些是具有适当字段/设置器/获取器的普通 POJO。

在 Java EE 规范的早期/早期版本之一中,“DTO”术语被错误地与“ValueObject”混合在一起,尽管它们的用途略有不同。

希望这可以帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top