gpt4 book ai didi

.net - CRUD样式的数据驱动的分布式.NET应用程序体系结构问题

转载 作者:行者123 更新时间:2023-12-04 22:51:49 27 4
gpt4 key购买 nike

上下文:在.NET平台上构建智能客户端应用程序,在该平台上您具有包含大量列的复杂数据库模型。自然的应用程序样式是典型的数据驱动的CRUD。在某些情况下,服务器端逻辑也相当合理,验证也有些复杂。您可以完全控制客户端和服务器,因此对互操作性的需求已降至最低。

这个问题有很多细节,对此表示歉意,但这是因为我想为答案设置适当的上下文。

其他一些假设
-在Microsoft世界中并不罕见,大多数以前的应用程序都是使用DataSets编写的,因此它是涉及开发人员的最著名技术。但是,可以说开发人员也非常熟悉面向对象的思想。
-您将需要在客户端和服务器上都运行验证。
-您不会以表格形式显示大多数数据。
-这不是Intranet应用程序,因此您不能过多地考虑带宽

最大的问题:数据集还是对象?



如果您使用数据集,则有一些正面和负面的信息
-积极的一面:从数据库中获取数据,通过网络获取数据以及通过网络以较小的块返回更改的数据方面,您会获得Microsoft的一点支持-因为您只能指定发送更改。发送较少的数据是一件好事,因为可能涉及大量数据。
-负面影响是:在验证,业务逻辑等方面,您将获得程序形式的代码,而您将无法获得面向对象的代码的好处–行为和数据并存,更自然的工作和思考方式您正在做什么,并且可能与验证逻辑有更紧密的联系。您还可以避开将数据集放在网格中的好处,因为这不是常见的用例。


如果您要寻找对象,那是相同的练习,但是涉及的选项更多:
积极因素:将行为和数据结合在一起。验证逻辑更紧密。更容易看到和理解对象之间的关系。更具可读性的代码。更容易进行单元测试。
但是,您还有很多选择和工作要做:

或/映射
-从关系模型到对象的数据获取。或映射器并不那么复杂,并且能够很好地处理它。但这增加了开发时间。

合约对应
-通常将数据从服务器端对象映射到合同对象(可能是DTO的对象)是一种很好的做法。由于此应用程序非常适合CRUD风格的体系结构,因此DTO并不会真正为图片增加太多价值,而只是绘制地图即可。

共享码
-您可以使用共享代码方案,其中包含域数据和逻辑的程序集在客户端和服务器端均可用。紧密耦合,但是如果您具有自然紧密耦合的客户端-服务器应用程序,则不一定坏。

无论您是否选择添加合同层,都有必须通过电线发送的大型对象结构
由于我们同时控制客户端和服务器,因此传输和编码应为基于TCP的二进制编码。那会有所帮助。
对于数据集,您可以选择仅将更改发送回。来回发送整个对象结构可能是性能问题。
发送整个对象结构的一种选择是,以某种方式识别所涉及的更改(创建,更新,删除),并仅发送有关该更改的信息。从理论上讲,将汇总根ID和更改发送到服务器并要求服务器延迟加载汇总根,执行所做的更改,然后再次保存,并不是很困难。但是所涉及的最大复杂性是确定所做的更改。您曾经尝试过这种方法吗?为什么?你到底是怎么做到的?


介绍
确切的UI技术对于这个问题并不是很重要,可以使用WinForms,Silverlight或WPF。让我们假设我们正在使用WPF,因为它是一个新的智能客户端。这意味着我们有两种方式绑定,并且可以正确使用MVVM。

绑定到用户界面中的对象将需要实现INotifyPropertyChanged并在每次更新属性时引发一个事件。您如何解决呢?如果您使用共享代码方案,则可以将其添加到域对象中,但这将涉及在服务器端添加代码和逻辑,而这些代码和逻辑决不该在此处使用。如果您使用合同对象,则分隔会更自然,但这仅仅是增加一层映射而没有太多的附加值。


技术领域
有一些可用的技术可以帮助解决某些问题,但通常会使其他问题复杂化。您是使用它们还是自己重新构建东西?
**
-CSLA是可能的,但是它使单元测试更加困难,并且似乎增加了与数据访问的紧密耦合。它确实可以解决许多问题,但是我个人对此技术没有能力,因此很难说它是否合适。
-Silverlight解决方案可以使用WCF RIA服务,但是肯定存在一些限制。数据大小为一。
-WCF数据服务是另一种快速解决问题的方法,但是REST并没有太大帮助,而且您还缺少RIA Services中提供的验证支持。


摘要
如果您走到了这一步,希望您对我的发展方向有所了解。我试图缩小范围以避免一次谈论所有内容,但是分布式开发很复杂,因此您必须考虑很多部分。



更新资料

谢谢你们的回应!我试图问的问题足够开放,可以回答各种问题,但具体程度足以应付一些不常见的要求。

有不同的考虑因素,各有其优缺点,并且因系统而异。通常,每种方法都会增加寻找解决方案的复杂性。这个问题的重点之一是要获得一些特别的答案,而这些额外的需求并不一定直接适合当今通常是正确的一个答案-使用基于任务的UI。如果您愿意,我不是“ CRUD家伙”。但是由于种种原因(通常是传统的),一些系统非常适合CRUD。

许多业务应用程序有类似的要求,它们的方向不同:

业务相关
-查看:向用户显示数据并更新相同的数据(读取和CUD-创建,更新,删除)
-验证:业务规则

UI相关
-验证:UI规则
-UI更新:特定于仅使UI更新对象更改的代码(INotifyPropertyChanged)

网络相关
-数据大小:您通过网络发送的数据量

数据库相关
-延迟加载

SRP /重用相关
-映射:由多层对象/分离的关注点引起

维护/变更相关
-更改:添加新信息(列/字段)
-代码量
-重用和“改变的理由”

技术局限性
-变更追踪

但是,这些只是一些非常具体的内容。您始终需要知道最重要的“故障”,以及所需的可扩展性,可用性,可扩展性,互操作性,可用性,可维护性和可测试性。

如果我想在大多数情况下归纳一些东西,我会说类似:

客户
-使用MVVM进行分离和可测试性
-在DTO之上创建VM
-在VM中实现INotifyPropertyChanged。
-使用XamlPowerToys,Postsharp或其他一些方法可以帮助解决此问题
-UI中的单独读取和CUD
-使CUDs基于任务,并使用命令或类似命令将这些操作发送到服务器端

服务器
-量身定制每个屏幕的dto
-或使用Ayende在http://msdn.microsoft.com/en-us/magazine/ff796225.aspx中描述的多查询方法
-使用自动映射可避免繁琐,手动且与您要解决的问题完全无关的事情,即映射是
-让域模型主要关注业务操作,包括与CUD相关的操作,而不是读取
-避免增加重用原因的可重用性
-避免封装问题
-(通过启用CQRS样式体系结构,并可能在时间上分别缩放读取和CUD)
-尝试找到一种适合应做的验证方法(请读:http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/02/15/validation-in-a-ddd-world.aspx

这是否是我在这种特定情况下要采取的方法?

好吧,这就是我想要开始讨论的问题:)但是,这似乎比我希望的要难(你们两个人除外)。

最佳答案

我只能从我们自己的经验中回答。我们尝试了不同的框架(WCF RIA,Ideblade),并得出结论,框架只会使情况变得更糟。我将进一步解释。

首先,您应该忘记CRUD。仅演示应用程序具有CRUD-实际应用程序具有行为。

我不建议在客户端模拟整个实体图。它们是两个分离的关注点。

您应该为每种情况创建量身定制的Dto。例如。假设您有一个OrderSearchView,然后创建一个OrderSearchDto并仅映射所需的字段。在EditOrderView中,您将改为使用EditOrderDto-仅包含所需字段。

我真的不建议在实体和dto之间使用自动映射工具。因为在dto和实体之间通常没有一对一的关系。 dto通常由不同的多个后端实体构建。映射是如此简单,所以我看不到映射框架的意义。而工作不是映射-它是编写单元测试-无论如何(无论有没有映射框架),您都必须这样做。

Dtos应该与客户端技术无关。在dto上实现INotifyPropertyChanged违反了单一职责原则。他们称之为数据传输对象。相反,您可以在客户端上创建演示者。您创建一个EditOrderPresenter,它是EditOrderDto的包装器。因此,dto只是EditOrderPresenter内部的私有成员字段。 Presenter是为在客户端层中进行编辑而量身定制的-因此它通常将实现INotifyPropertyChanged。 EditOrderPresenter通常具有与dto相同的属性名称。

您应该在物理上将客户端验证与服务器端的实体验证分开。当心分享!我认为客户端验证只是GUI的调整-使GUI体验更好。不要在dto和实体之间共享验证代码很重要-它可能比有用性引起更多的麻烦。只要确保您始终在服务器端进行验证,无论在客户端进行哪种验证即可。验证有两种:简单的属性验证和整个实体验证(dto也是如此)。实体验证应仅在状态转换时执行。请查看Jimmy Nilssons域驱动设计以获取背景知识。我不建议使用验证规则引擎-仅使用状态模式。

那更新,插入,删除呢?在我们的实现中,我们使用WCF,并且WCF API只有一种方法:IResponse [] Process(params IRequest [] requests);
这到底是什么意思?这意味着客户端正在向服务器发出一批请求。在服务器端,您为系统中定义的每个请求实现RequestHandler。然后,您返回响应列表。确保Process()方法是一个工作单元(〜一个事务)。这意味着如果批处理中的一个请求失败-所有请求都将失败-这将导致事务回滚-并且不会对数据库造成损害。 (不要在responsehandlers中使用错误代码-而是强制转换异常。)

我建议您窥视Agatha消息传递服务器。戴维·布瑞恩(Davy Brion)关于消息传递层的博客文章很棒。在我们公司中,我们选择实现自己的消息传递服务器-因为我们不需要Agatha提供的所有功能,因此我们对语法进行了一些改进。无论如何,实现消息传递服务器并不是真的很困难-这是一种很好的学习体验。链接http://davybrion.com/blog/

那您要如何处理Dto。好吧,您永远不会更新它们,而是在客户端上更改它们,以便获得对gui的正确反馈。因此,您可以使演示者以正确的顺序跟踪dto(要求)发生的所有事情。这将是您的requestBatch。然后将requestbatch发送到WCF上的process-command-然后将在服务器端“重播”请求并由请求处理程序处理。
这实际上意味着您永远不会更新dto。但是演示者可以在客户端编辑dto,以提供适当的gui反馈。演示者的工作还在于跟踪所有已完成的编辑,以便将它们作为requestbatch(重录的顺序与编辑顺序相同)发回到服务器。
考虑以下情况,您检索一个现有订单,进行编辑,然后将更改提交回数据库。这将导致两个批次,一个批次用于获取订单,另一个批次用于将更改提交回去。
RequestBatch 1:GetOrderByIdRequest

(..然后用户编辑数据..)

第2批需求:
StartEditOrderRequest,状态更改以编辑方式,放松验证
AddConsigneeToOrderRequest
ChangeEarliestETDOnOrderRequest,还不需要验证最新的ETD!
DeleteOrderlineRequest
ChangeNumberOfUnitsOnOrderlineRequest
EndEditOrderRequest,状态更改为原始状态,请在此处执行实体验证!
GetOrderByIdRequest,以便使用最新更改来更新gui。

在服务端,我们使用NHibernate。 Nhibernate使用一级缓存来避免繁重的数据库负载。因此,同一工作单元(requestbatch)中的所有请求都将使用缓存。

每个请求只应包含完成任务的最少数据量。这意味着使用OrderId +其他一些属性而不是整个dto。关于乐观更新,您可以将一些oldValues与请求一起发送-这称为并发集。请记住,并发集通常不包含很多字段。因为在此期间已更改的更新顺序不一定意味着您将有加薪条件。例如。在此期间,如果收货人被其他用户编辑,则添加和订购行并不表示您有加薪条件。

好吧,这不会导致大量的工作。当然,您将拥有更多的课程,但每个课程都很少,而且只有一个职责。

顺便说一句,我们在一个中型项目中尝试了WCF RIA服务。而且进展不顺利。我们必须找到围绕框架的方法(hack)来完成我们想要的事情。而且它还基于代码生成-这对于构建服务器来说是非常糟糕的。另外,您永远都不应通过图层来查看。您应该能够在不影响客户端层的情况下更改支持的实体。使用RIA很难。我认为OData与WCF RIA属于同一类别。

如果您需要在客户端上构建查询,则可以使用规范模式-不要使用iqueryable-那么您将独立于后端实体。

祝好运。
推特:@lroal

关于.net - CRUD样式的数据驱动的分布式.NET应用程序体系结构问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3823016/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com