【问题标题】:Tracking changes in complex object graph跟踪复杂对象图中的变化
【发布时间】:2011-03-27 12:58:51
【问题描述】:

我开始考虑在断开连接的应用程序中跟踪复杂对象图的变化。我已经找到了几个解决方案,但我想知道是否有任何最佳实践或您使用什么解决方案以及为什么?我将同样的问题传递给MSDN forum,但我只收到一个答案。我想从其他开发人员的经验中获得更多答案。

这个问题与 .NET 有关,因此对于实现细节的答案,我更喜欢与 .NET 世界相关的答案,但我认为这在其他平台上是相同的。

我的案例中的理论问题在多层架构(目前不一定是n层)中定义如下:

  • 使用 ORM 处理持久性的存储库层(ORM 工具目前无关紧要,但很可能是 Entity Framework 4.0 或 NHibernate)。
  • 表示域对象的纯类集(持久无知 = POCO,相当于 Java 世界中的 POJO)。存储库保存这些类并将它们作为查询结果返回。
  • 与域实体一起工作的域服务集。
  • 定义业务逻辑网关的外观层。它在内部使用存储库、域服务和域对象。域对象不暴露——每个外观方法使用一组专门的数据传输对象作为参数和返回值。将域实体转换为 DTO 是每个外观方法的责任,反之亦然。
  • 使用外观层和 DTO 的现代 Web 应用程序 - 我称之为断开连接的应用程序。一般来说,未来设计可能会发生变化,因此外观层将被 Web 服务层包裹,而 Web 应用程序将使用该服务 => 过渡到 3 层(Web、业务逻辑、数据库)。

现在假设域对象之一是 Order,它具有 Order 详细信息(行)和相关 Orders。当客户请求编辑订单时,它可以修改订单、添加、删除或修改任何订单详细信息以及添加或删除相关订单。所有这些修改都是在 Web 浏览器中的数据上完成的——javascript 和 AJAX。因此,当客户端按下保存按钮时,所有更改都会一次性提交。问题是如何处理这些变化?存储库和 ORM 工具需要知道哪些实体和关系被修改、插入或删除。我以两个“最佳”解决方案结束:

  1. 将 DTO 的初始状态存储在隐藏字段中(在最坏的情况下存储到会话中)。当收到保存更改的请求时,根据收到的数据创建新的 DTO,并根据持久的数据创建第二个 DTO。合并这两个并跟踪更改。将合并的 DTO 发送到外观层,并使用收到的有关更改的信息来正确设置实体图。这需要在域对象中进行一些手动更改跟踪,以便可以从头开始设置更改信息,然后将其传递到存储库 - 这是我不太满意的一点。

  2. 根本不跟踪 DTO 中的更改。在外观层接收修改的数据时,创建修改的实体并从存储库加载实际状态(通常是对数据库的附加查询 - 这是我不太满意的一点) - 合并这两个实体并通过 ORM 工具提供的实体代理自动跟踪更改(实体框架 4.0 和 NHibernate 允许这样做)。并发处理需要特别注意,因为实际状态不必是初始状态。

你怎么看?你有什么建议吗?

我知道可以通过在某些应用程序层上使用缓存来避免其中一些挑战,但这是我目前不想使用的东西。

我对这个话题的兴趣更加浓厚。例如,假设应用程序采用 3 层架构并且客户端(Web 应用程序)不会用 .NET 编写 = DTO 类不能被重用。跟踪 DTO 上的更改会困难得多,因为这需要其他开发团队在他们的开发工具中正确实施跟踪机制。

相信很多应用都需要解决这些问题,请分享经验。

【问题讨论】:

    标签: .net architecture orm dto


    【解决方案1】:

    一切都与责任有关。

    (我不确定这是否是您想要的答案 - 如果不是,请告诉我,以便我更新它)。

    因此,我们在系统中有多个层 - 每个层负责不同的任务:数据访问、UI、业务逻辑等。当我们以这种方式构建系统时,我们(除其他外)试图做出未来的改变通过让每个组件负责一项任务来轻松实现——这样它就可以专注于该一项任务并做好它。随着时间的推移和需要更改,它还可以更轻松地修改系统。

    在考虑 DTO 时需要牢记类似的想法 - “如何跟踪更改?”例如。我是这样处理的: BL 负责管理规则和逻辑;鉴于网络的无状态特性(这是我大部分工作的地方),我只是没有跟踪对象的状态并明确地寻找变化。如果用户将数据传回(要保存/更新),我将全部传回而不关心更改了什么。

    一方面这可能看起来效率低下,但由于数据量不是很大,所以这不是问题;另一方面,由于过程更简单,因此“活动部件”更少,出错的可能性也更少。

    我如何将数据传回? -

    • 我使用 DTO(或者 POCO 可能更准确);当我在 BL 和 DAL 之间交换数据(通过接口/DI)时,数据作为 DTO(或它们的集合)进行交换。具体来说,我将结构用于单个实例,将这些结构的集合用于多个。

    • DTO 定义在一个公共类中,几乎没有依赖关系。

    • 我故意尝试限制为特定对象(如“订单”)创建的 DTO 的数量 - 但同时,如果有充分的理由,我会创建新的。通常我会有一个“胖”DTO,其中包含该对象的大部分/所有可用数据,我也可能有一个更精简的设计用于集合(用于列表等)。在这两种情况下,这些 DTO 都是纯粹的,用于返回“阅读”信息。您必须牢记责任——当 BL 请求数据时,它通常不会同时尝试写回数据;因此,DTO 是“只读”这一事实更多地是为了符合干净的接口和架构,而不是业务规则。

    • 我总是为插入和更新定义单独的 DTO——即使它们共享完全相同的字段。这样一来,最糟糕的情况就是重复一些琐碎的代码 - 而不是需要解开依赖关系和多个重用案例。

    最后 - 不要混淆 DAL 的工作方式和 UI 的工作方式;让 ORM 做他们的事,仅仅因为他们以给定的方式存储数据并不意味着它是唯一的方式。

    最重要的是在层之间指定有意义的接口。

    管理变化是 BL 的工作;让 UI 以最适合您的用户的方式工作,并让 BL 弄清楚它想如何处理它,而 DAL(通过您与DI 的漂亮干净的界面)只是按照它的指示行事。

    【讨论】:

    • 您好,非常感谢您的回答。我喜欢。它非常接近我的架构,除了在我的情况下,我在 UI 和 BL 之间使用 DTO,在 BL 和 DAL 之间使用域对象/实体。我理解您的解释,我唯一缺少的一点是主要问题的答案 - 如何跟踪复杂对象图中的变化?你收到 DTO,你必须以某种方式找到发生了什么变化。如果您的 DTO 是简单对象,您不需要跟踪它,但是包含其他 DTO 集合的 DTO 呢?您需要知道该集合中发生了什么变化。
    • RE 对象图的变化——我希望有一些设计模式可以处理这个问题,但我想不起来——想到了 Momento,否则可能是事件驱动模型?
    • 在传统的 ASP.NET 模型中,页面往往每次都从头开始构建(因此无论发生什么变化,您都在加载所有数据),并且如果您在viewstate 您倾向于将其视为“数据” - 所以我一直不想以您建议的方式寻找特定的变化(?)。另一种方法是基于 AJAX 的方法,但这是类似的。观察者模式也可能很有趣。
    【解决方案2】:

    我们的架构与您的架构非常相似,但在客户端和服务器上使用包含相同域对象(不确切 - 代码是共享的)的 Silverlight 客户端。简而言之,我们架构的关键点

    • 客户端具有域模型,并且使用我自己实现的跟踪框架来跟踪更改(它使用 AOP,因此我可以在客户端使用 POCO;我不知道用于此的框架,我希望域模型能够坚持无知)
    • 整个模型存储在客户端的一种远程存储库中。当我们保存这些更改时,将提取更改树(通过我的更改跟踪框架)并使用汇编程序将其转换为 DTO(确切地说是 DataContracts,但这无关紧要)。 DTO 有一个跟踪状态标志(新、已修改、已删除)。
    • 在服务器端(服务层由 WCF Web 服务实现),DTO 被转换为域对象并附加到 ORM(在我们的例子中为 NHibernate)。由于这个附件过程,我需要跟踪状态。可以通过 ORM 完成和持久化其他更改

    目前很难将复杂的图表附加到 ORM,但我希望原因是我在使用 NHibernate 方面没有太多经验。

    我们还没有完成,但看起来很有希望。

    对于数据访问,我们尝试使用 WCF 数据服务。但我不认为我们会使用它们,因为需要使用 DataContract。这导致从基于 DataContract 的 LINQ 查询转换为基于域对象的 LINQ 查询。如果域模型和数据契约差异很大(在某些迭代中会出现这种情况),那么这将不方便且难以实现。

    有什么注意事项吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2012-11-17
      • 2011-11-07
      相关资源
      最近更新 更多