.NET中的分布式DDD:与客户端共享域对象

时间:2011-05-02 13:19:44

标签: .net domain-driven-design soa

我正在开发一个3层应用程序(不是3层!),其中一个客户端应用程序在一个层(物理集群)上运行,该应用程序与另一个层上运行的服务应用程序和另一个层上的数据库服务器交互。该应用程序有很多业务规则,流程逻辑等,我认为应该可以在应用程序层和服务层上使用,以改善用户体验,减少对服务的调用以及消除冗余编码。

让我们使用这个例子:在我的域层中,我有一个Document对象。此对象包含AllowPublish属性,该属性检查对象的内部状态,如果状态允许发布文档,则返回true / false。该对象还有一个Publish方法,它修改对象的内部状态,以反映通过将IsPublished标志设置为true并引发Published域事件来发布它的事实。

我有一个单独的AuthorizationService,它确定是否允许当前用户发布以及将对象持久保存到数据库的DocumentRepository。

在我的服务应用程序中,我的DocumentService有一个PublishDocument方法,它接受文档id,使用id从存储库中检索文档,检查AllowPublish属性,如果为true,则调用Publish,然后使用存储库保存更新的对象。

我在客户端上的行为略有不同。在这种情况下,我使用AllowPublish属性来启用/禁用命令按钮。启用并单击后,我调用一个服务代理,该代理公开一个接受文档ID的PublishDocument方法。代理将调​​用传递给服务应用程序的同名DocumentService方法。

为了消除重复代码,共享业务逻辑,验证规则等,我已将域对象放置在单独的程序集中,由客户端应用程序和服务应用程序共享。这意味着客户端应用程序现在可以访问我的Document类的Publish方法,即使它只是相关的,并且只应由我的服务应用程序使用。这让我重新考虑我正在采取的整个方法。

虽然我理解使用DTO在客户端和服务器之间传递状态,但我使用的是.NET 3.5,据我所知,共享程序集是与客户端共享业务和验证规则的唯一方法应用。我有一些想法,我可以去其他方向,但希望在走上新的道路之前得到一些建议。

另一方面,我对客户端的当前实现采用了我认为是一种全面的授权方法,这可能只是一个不同模型会更好的指标。就像我在我的服务器端服务应用程序中有一个AuthorizationService,DocumentService用来执行授权,我有一个类似于我的客户端代码使用的代理。这意味着我需要在客户端代码中使用另一层间接来支持授权,可能是Controller或ViewModel。如果用例是有效的,那么这很好。

修改

我可能需要澄清,在编辑文档时,AllowPublish属性是动态的。首次检索时,它可能是错误的,但在满足业务规则时将变为真。在客户端应用程序中运行业务规则可以让我们提供更丰富的用户体验。

3 个答案:

答案 0 :(得分:5)

为了关闭以及将来遇到这篇文章的任何人,我想我会分享我最终得到的东西,同时归功于Iulian帮助引导我朝着正确的方向前进。

简单地说,我意识到(在Iulian的帮助下)我确实在客户端应用程序和服务器端服务应用程序之间有两个不同的用例。结果,我咬了嘴,为每个人创建了单独的域模型。

我的思维过程的一部分是逻辑上分离应用程序本身。虽然客户端应用程序无法在没有服务应用程序的情况下运行,但我调整了我的思路,将此关系视为数据访问层而不是应用程序和域层。与此同时,我在服务器端转移了视图,将服务接口视为该应用程序的表示层。因此,拥有不同的域对象/层非常有意义。

不幸的是,这确实需要权衡。我希望尽可能地将技术推进到最小化以利用RIA服务,这将允许我们将数据注释从服务器传递到客户端。但是,现在,我使用简单的DTO对象在应用程序和RESTful服务接口之间传递状态信息,该接口将API提供给核心域逻辑。

使用我最初引用的示例,我有以下设置:

当用户单击UI中的“发布”按钮时,客户端应用程序将在我的服务代理类上调用Publish方法。服务代理处理与服务器端服务的通信。在这种情况下,DocumentService公开一个Publish方法,该方法接受要发布的Document的ID(以及用户信息等)

DocumentService从DocumentRepository中检索Document域对象,并在对象上调用Publish方法,该方法更新Document的内部状态。然后,该服务调用DocumentRepository上的Update方法,传入更新的Document对象,并将更改保留到数据库中。

权衡是我需要有逻辑/规则来确定是否以及何时可以在客户端和服务器上发布文档(因为我们不能假设请求始终有效)。同样,将解决方案视为具有自己的一组用例的两个独立应用程序,有助于使其更合理(在我看来)。如您所见,我不需要在客户端版本的Document中使用Publish方法,但我确实需要更改跟踪以获得丰富的用户体验。我不需要在服务器上进行相同类型的更改跟踪,因为在更改对象时没有UI刷新。在后一种情况下,ORM实现了变更跟踪,以使持久性更加优化。

因此,对我来说,底线是将解决方案分离到不同的应用程序中,这使我能够隔离用例并绘制出每个应用程序所需的适当对象和关系以满足其目的。现在我有一个支持多个客户端的解决方案,因为我已经将客户端与服务器分离,允许我插入新客户端而无需更改服务应用程序或现有客户端应用程序。

HTH

答案 1 :(得分:3)

您不应将域模型对象放在客户端中。直接在客户端使用它们将限制您在未来迭代中进化域的能力,以及在进行DDD时,当您从域专家那里获得更深入的见解时,进化域的能力至关重要。

我不知道在您的情况下这是否可行,但也许您可以将业务规则分解为一些策略对象,这些对象只会具有非常特定的行为,可以在域模型和客户端中使用。如果您的目标是避免逻辑重复并且您需要的行为完全相同 - 这可能不是这种情况,那么这可能没问题。在您的客户端中,您可能需要一些额外的验证步骤,这些步骤可能与您在域模型中所需的步骤不同。

如果可以在ViewModel中进行客户端验证,可能最好的解决方案是使用MVC或MVVM模式,如果可能的话,基于某些共享规则。

我客人的主要想法是不要为了DRY而结合概念。像往常一样,乌迪达汉作为一篇文章:The Fallacy Of ReUse

答案 2 :(得分:0)

考虑使用InternalsVisibleTo属性。