面向服务的架构和领域驱动设计

23
我一直以SOA的方式开发代码。今年我一直在尝试做更多的DDD,但我一直觉得我还没有完全理解它。在我们的工作系统中,我们的负载均衡和设计都是无状态的。整体架构如下:
网站
===物理层==
主服务
==物理层==
服务器1 / 服务2 / 服务3 / 服务4
只有服务器1、服务2、服务3和服务4能够访问数据库,并且主服务根据所订购的产品调用正确的服务。每个物理层也都平衡了负载。
现在当我开发新服务时,我试图使用DDD思想来开发,尽管它并不像它适合这样做。我使用良好的DDD原则,如实体、值类型、存储库、聚合、工厂等。
我甚至尝试过使用ORM,但它们似乎并不适用于无状态架构。我知道有办法解决,例如使用IStatelessSession而不是带有NHibernate的ISession。但ORM感觉不适合无状态架构。
我注意到我只使用了DDD教给我的一些概念和模式,但整体的架构仍然是SOA。我开始认为DDD并不适合大型系统,但我认为其中一些模式和概念确实适用于大型系统。
就像我说的,也许我没有完全掌握DDD,或者可能我在过度分析我的设计?也许通过使用DDD教给我的模式和概念,我正在使用DDD?对于这篇文章,可能没有真正的问题,而只是我在思考DDD适用于整体系统的位置以及其真正的可扩展性。事实上,我认为我甚至不知道DDD是什么?
4个回答

24

我认为一个普遍的误解是SOA和DDD是两种相互冲突的风格。

在我看来,它们是两个很好地结合在一起的概念; 你可以创建一个封装了你的领域概念的领域模型,并通过服务将入口点暴露到该模型中。

我也不明白ORM和服务存在什么问题,你可以轻松地为每个服务调用使用会话/工作单元。 只需将你的服务操作建模为原子领域命令即可。

一个简单的例子:

[WebMethod]
void RenameCustomer(int customerId,string newName)
{
    using(var uow = UoW.Begin())
    {
        var customerRepo = new CustomerRepo(uow);
        var customer = customerRepo.FindById(customerId);
        customer.Rename(newName);
        uow.Commit();
    }
}

也许你面临的问题是创建了像“UpdateOrder”这样的服务,它需要一个订单实体并尝试在新会话中进行更新?

我尝试避免那种服务,而是将其拆分为更小的原子命令。

每个命令都可以作为操作公开,或者您可以有一个单一的服务操作,接收命令组,然后将其委派给命令处理程序。

在我看来,这样可以更好地表达您的意图。


我终于发布了一些关于这个的帖子:服务+命令模式+DDD http://rogeralsing.com/2013/12/02/using-command-pattern-to-capture-language-and-intent-for-services/ - Roger Johansson
还有一些信息,说明旧的DTO方法为什么不好。http://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/ - Roger Johansson
当你在示例中有两种相同的方法来应用相同的业务逻辑时,比如Service.RenameCustomer(ID, Name)Customer.Rename(Name),它们是如何协同工作的?在更复杂的情况下,即使这两个重复的方法也会有两个不同类的参数,例如NameDTOName - Lightman

10
领域驱动设计最重要的是大局观念:
  • 通用语言,
  • 战略决策,通过在核心领域中添加价值工作(并使自己远离其他令人讨厌的系统),以及
  • 通过将基础架构与业务逻辑分离来制定可测试、灵活的设计方法。
这些思想广泛适用,也是最有价值的部分。
关于工厂、服务、存储库、聚合等设计模式的内容很多,我认为它们只是一位经验丰富的开发者向另一位提供的建议,而不是教义,因为这些设计模式很大程度上取决于您使用的语言和框架。在我看来,它们往往被过分强调,因为像我们这样的程序员喜欢细节,并且会在那种东西上执着。其中也包含了有价值的东西,但需要保持透视。因此,某些内容可能对您不太相关,或者在您使用它时可能会增长。
所以我认为,这并不像有一个检查清单可以确保您使用所有的模式,而是要记住大局,并看看它如何改变您开发软件的方法。并且,如果您从这些模式中获得了一些好的提示,那也很棒。
具体来说,关于SOA的问题,我已经开发过将所有状态推迟到数据库的应用程序,这些应用程序使用了持久化无关的领域对象。为服务编写必须模拟daos并反馈内容的测试是枯燥乏味的,我可以将更多逻辑放入领域对象中,这样我就不必在服务中操纵模拟了,所以我更喜欢这种方法。

9
使用DDD引入的一些概念在构建SOA时可能会让您感到困惑。
我完全同意另一个答案所说的,SOA服务公开作为原子命令的操作。我认为非常干净的SOA使用消息而不是实体。然后,服务实现将利用领域模型来实际执行操作。
但是,在DDD中有一个称为“领域服务”的概念。这与应用程序服务略有不同。通常,“领域服务”是在与领域模型的其他部分相同的通用语言中设计的,并表示不适合实体或值的业务逻辑。
领域服务不应与应用程序服务混淆。实际上,应用程序服务很可能被实现为使用领域服务。毕竟,应用程序服务可以在SOA中完全封装领域模型。

7
我真的很晚才了解到这一点,但我想在其他人非常好的答案中添加以下内容。
  • DDD与SOA没有任何冲突。相反,DDD可以帮助您维护更好的面向服务的架构。 SOA推广服务的概念,因此您可以定义更好的边界(哦,上下文边界也是DDD的概念!)以及改善对它们的理解。
  • DDD不是应用一组模式(例如存储库、实体等)。DDD主要是尝试对软件进行建模,使得所有概念(即面向对象编程中的类)直接与业务概念对齐。

您还应该查看此视频(特别是最后5分钟),Eric Evans在其中讨论了这个话题。


我甚至尝试过使用ORM,但它们似乎并不适合无状态架构。

我手头没有任何参考资料来支持这一点。但是,您是正确的,ORM也不适合DDD。这是因为它们试图弥合对象-关系不匹配,但是方法不正确。它们将软件强制转向贫血领域模型,其中类最终成为“纯数据持有者”。

我开始认为DDD不适合大型系统,但我确实认为 一些模式和概念适用于大型系统。

在我上面链接的视频中,您还可以找到Eric解释说,DDD概念可能会在非常大规模的系统中“破裂”。例如,想象一个零售系统,每个订单都是包含数千个订单项的聚合。如果您想要按照DDD严格计算订单的总金额,您必须在内存中加载所有订单项,这与利用存储系统(例如巧妙的SQL语句)相比极其低效。因此,应该始终牢记这种权衡,DDD不是万能药。

就像我说的,也许我没有掌握DDD,或者我过度分析了我的设计?

对不起,但我将再次引用Eric Evans的话。正如他所说,DDD并不是为完美主义者而设计的,这意味着可能存在理想设计不存在的情况,您可能不得不选择一种建模方案,从建模角度来看更差。要了解更多相关信息,可以查看此文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接