跟踪复杂对象图中的变化

发布于 2024-09-14 03:00:57 字数 1646 浏览 2 评论 0原文

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

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

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

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

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

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

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

你对此有何看法?你有什么建议吗?

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

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

我相信这些问题在很多应用中都得到了解决,请分享您的经验。

I started to think about tracking changes in complex object graph in disconnected application. I have already found several solutions but I would like to know if there is any best practice or what solution do you use and why? I passed same question to MSDN forum but I received only single answer. I would like to have more answers to learn from experience of other developers.

This question is related to .NET so for answers with implementation details I prefer answers related to .NET world but I think this is the same on other platforms.

The theoretical problem in my case is defined in multi layered architecture (not necessarily n-tier at the moment) as follows:

  • Layer of repositories using ORM to deal with persistence (ORM tool doesn't matter at the moment but it will most probably be Entity Framework 4.0 or NHibernate).
  • Set of pure classes (persistent ignorant = POCO which is equivalent of POJO in Java world) representing domain objects. Repositories persists those classes and returns them as results of queries.
  • Set of Domain services working with domain entities.
  • Facade layer defining gateway to business logic. Internally it uses repositories, domain services and domain objects. Domain objects are not exposed - each facade method uses set of specialized Data transfer objects for parameter and return value. It is responsibility of each facade method to transform domain entity to DTO and vice-versa.
  • Modern web application which uses facade layer and DTOs - I call this disconnected application. Generally design can change in future so that Facade layer will be wrapped by web service layer and web application will consume that services => transition to 3-tier (web, business logic, database).

Now suppose that one of the domain object is Order which has Order details (lines) and related Orders. When the client requests Order for editation it can modify Order, add, remove or modify any Order detail and add or remove related Orders. All these modifications are done on data in the web browser - javascript and AJAX. So all changes are submited in single shot when client pushes the save button. The question is how to handle these changes? Repository and ORM tool need to know which entities and relationships were modified, inserted or deleted. I ended with two "best" solutions:

  1. Store initial state of DTO in hidden field (in worse case to session). When receiving request to save changes create new DTO based on received data and second DTO based on persisted Data. Merge those two and track changes. Send merged DTO to facade layer and use received information about changes to properly set up entity graph. This requires some manual change tracking in domain object so that change information can be set up from scratch and later on passed to repository - this is the point I am not very happy with.

  2. Do not track changes in DTO at all. When receiving modified data in facade layer create modified entity and load actual state from repository (generally additional query to database - this is the point I am not very happy with) - merge these two entities and automatically track changes by entity proxy provided by ORM tool (Entity framework 4.0 and NHibernate allow this). Special care is needed for concurrency handling because actual state does not have to be the initial state.

What do you think about that? What do you recommend?

I know that some of these challenges can be avoided by using caching on some application layers but that is something I don't want to use at the moment.

My interest in this topic goes even futher. For example suppose that application goes to 3-tier architecture and client (web application) will not be written in .NET = DTO classes can't be reused. Tracking changes on DTO will than be much harder because it will require other development team to properly implement tracking mechanism in their development tools.

I believe these problems have to be solved in plenty of applications, please share you experience.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

彡翼 2024-09-21 03:00:58

一切都与责任有关。

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

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

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

一方面,这似乎效率低下,但由于数据量并不大,这不是问题;另一方面,“移动部件”更少,出错的可能性也更少,因为过程要简单得多。

我如何将数据传回? -

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

  • DTO 是在一个具有很少依赖性的公共类中定义的。

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

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

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

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

管理变更是 BL 的工作;让 UI 以最适合您的用户的方式工作,让 BL 弄清楚它想要如何处理这个问题,以及 DAL(通过您漂亮干净的界面 DI) 只是按照它的指示去做。

It's all about responsibility.

(I'm not sure if this is the sort of answer you're after - let me know if it's not so I can update it).

So we have multiple layers in a system - each is responsible for a different task: data access, UI, business logic, etc. When we architect a system in this way we are (amongst other things) trying to make future change easy by making each component responsible for one task - so it can focus on that one task and do it well. It also makes it easier to modify the system as time passes and change is neeed.

Similar thoughts need to be in mind when considering the DTO - "how to track changes?" for example. Here's how I approach it: The BL is responsible for managing rules and logic; given the stateless nature of the web (which is where I do most of my work) I'm just not tracking the state of an object and looking explicitly for changes. If a user is passing data back (to be saved / updated) I'll pass the whole lot back without caring what's been changed.

One one hand this might seem inefficient but as the amounts of data aren't vast it's just not an issue; on the flipside, there's less "moving parts" less can go wrong as the process is much simpler.

How I pass the data back? -

  • I use DTO's (or perhaps POCO's would be more accurate); when I exchange data between the BL and DAL (via interfaces / DI) the data is exchanged as a DTO (or collection of them). Specifically, I'm using a struct for a single instance and a collection of these structs for multiple.

  • The DTO's are defined in a common class that has very few dependencies.

  • I deliberately try to limit the number of DTO's a create for a specific object (like "Order") - but at the same time I'll make new ones if there is a good reason. Typically I'll have a "fat" DTO which contains most / all of the data available for that object, I'll also probably have a much leaner one that's designed to be used in collections (for lists, etc). In both cases these DTO's are pureyl for returning info for "reading". You have to keep the responsibilities in mind - when the BL asks for data it's usually not trying to write data back at the same time; so the fact that the DTO is "read only" is more about conforming to a clean interface and architecture than a business rule.

  • I always define seperate DTO's for Inserting and Updating - even if they share exactly the same fields. This way the worst that can happen is duplication of some trival code - as opposed to having dependancies and multiple re-use cases to untangle.

Finally - don't confuse how the DAL works with how the UI does; Let ORM's do their thing, just because they store the data in a given way doesn't mean it's the only way.

The most important thing is to specify meaningful interfaces between your layers.

Managing what's changed is the job of the BL; let the UI work in a way that's best for your users and let the BL figure out how it wants to deal with that, and the DAL (via your nice clean interface with DI) just does what it's told.

£冰雨忧蓝° 2024-09-21 03:00:58

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

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

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

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

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

有什么注意事项吗?

our architecture is very similar to yours, but using a Silverlight client containing the same domain objects (that no exact - the code is shared) on client and server. The key points of our architecture is in short

  • The client has the domain model and the changes are tracked using my own implemented tracking framework (it uses AOP so that I can use POCOs on the client side; I do not know a framework for that and I want the domain model to be persistent ignorant)
  • This whole model is stored in a kind of remote repository on the client. When we save those changes, a changes tree will be extracted (by my change tracking framework) and translated in DTOs (exactly DataContracts but that doesn't matter) using assemblers. The DTOs have a tracking state flag (new, modified, deleted).
  • On the server side (the service layer is implemented by WCF webservices) the DTOs are translated to domain objects and attached to the ORM (NHibernate in our case). Because of this attachmentent process I need the tracking state. Than additional changes can be done and persisted via ORM

Currently it is difficult to attach complex graphs to the ORM but I'll hope the reason is that I we do not have much experience in using NHibernate.

We're not finished yet but it seems to be promising.

For data access we tried to use WCF Dataservices. But I don't think that we are going to use them because a requirement is using DataContract. That leads to a translation from DataContract based LINQ queries to domain object based LINQ queries. That is not handy and too difficult to implement if domain model and datacontracts differ very much (that will be the case in some iterations).

Any considerations?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文