在Airwallex,领域驱动设计(DDD)方法被用来指导我们的工程师如何对复杂的业务问题和系统设计建模。在这篇博客中,我们提供了一个全面的工作流,我们使用DDD模式进行建模,然后对支付系统进行落地。
简介
全球支付系统是复杂和不断变化的,涉及从订单、欺诈、通知、与各种支付方式的集成到清算和结算等广泛的板块。
在处理复杂的系统时,大多数开发人员可能会遇到一些一致的问题:
- 责任重叠:在一个应用程序上使用一系列模型和战略意味着边界和职责不清晰。
- 没有隔离和模块化:复杂的业务工作流程和流程融合在一起并难以扩展。
- 核心业务没隔离:核心业务和战略会给技术实施带来挑战,这使得问题更加复杂。
在Airwallex,领域驱动设计(DDD)方法被用来指导我们的工程师如何解决复杂的业务问题和系统建模。
然而,DDD只是各种模式的集合,很难将其应用于系统设计。在这篇博客中,我们提供了一个全面的工作流程,介绍了我们是如何在Airwallex应用领域驱动设计的,从中得到的经验教训,以及我们接下来要做什么。
什么是DDD?
领域驱动设计(由Eric Evans提出)是一组帮助基于业务领域的底层模型设计软件系统的思想、原则和模式。DDD有两个不同的空间,问题空间和解决方案空间。
在问题空间中,您使用战略模式定义系统的顶层的系统层次,这些战略模式关注域、子域和通用语言的分析。
在解决方案空间中,采用战术模式来提供一组设计模式,您可以使用这些设计模式创建领域模型。这些模式包括有界上下文、上下文映射、实体、聚合、领域事件、领域服务、应用程序服务和基础设施。这些战术模式将帮助您设计既松散耦合又具有内聚性的微服务。
在实战中怎么使用DDD
下面是一个常见的案例:
一位顾客想在该商家的网站上购买一件价格为10美元的t恤。顾客可以用多种支付方式来支付这件t恤,比如Visa卡或微信钱包。在客户支付后,商家会从支付网关收到一个通知,显示客户的支付已经成功。然后,商家可以在Airwallex webapp中查看支付细节,包括购买价格、商家费用以及资金将何时进入Airwallex Global Account钱包。
操作流程
- 1.分析真实的业务用例来计算出问题空间中的领域和子域。通常,事件风暴是最佳实践。
- 2.在解决方案空间中定义边界上下文。
- 3.在边界上下文中,应用战术DDD模式来定义实体、聚合、域服务、域事件等。
- 4.使用步骤3的结果来确定团队中的微服务。
下面是分析结果。
问题空间
领域
支付系统
子域
付款处理:商家可以通过各种付款方式接受客户的付款。
金融:清算和解决商家的付款资金。
通用语言
付款意向:商家创建的订单的价格,产品,客户等。
付款尝试:商家创建的交易以获得订单的客户。
付款方式:客户支付产品的方式。
付款结算:付款之 后钱进入商家钱包。
付款视图:汇总付款详细信息视图,包含与一笔付款相关的所有数据。
解决问题空间
有界上下文(BC)限定了域模型的范围。根据对问题空间的分析结果,我们可以定义以下边界上下文:
支付网关: API网关,为商家提供restful API来创建或查看支付。
支付核心模块: 支付意图,方法资源管理。
支付适配器: 与一个外部的PSP集成,例如微信,支付宝,Visa,万事达等。
支付结算: 计算并结算商户每次支付的原则和费用。支付融合:支付明细汇总视图。
下面是生成的上下文映射的一个示例:
领域模型
从上面分析的场景和通用语言中,我们可以确定以下聚合、实体、值对象和域事件:
领域服务
根据我们的经验,领域服务为单个聚合使用业务逻辑服务,遵循单一责任。通常,我们将封装领域仓储、聚合修改和在领域服务中发布的领域事件。以PaymentAttemptExecutorService为例:
领域事件
领域事件可以使系统更具有可扩展性,并避免任何耦合,且一个聚合不应该决定其他聚合应该做什么。
例如,当PaymentCaptureCommand命令将支付状态更改为已支付时,会发出领域事件PaymentAttemptCapturedEvent。在PaymentAttemptCapturedEvent的领域事件处理程序(EventHandler)中,我们可以在该业务逻辑上加上你想要的逻辑。例如,通知支付聚合有界上下文更新支付详情,通知支付结算有界上下文计算结算金额和费用。
基础设施
在DDD模式中,基础设施层作用于将核心业务领域与技术实现细节分开。该层通常采用ACL (anti - corruption-layer)模式。以领域仓储为例:
领域仓储只定义接口功能,但实现细节应该隐藏在基础设施层中,细节上你可以使用PostgreSQL或MongoDB来保存数据。例如,在基础设施层中,PaymentAttemptPgRepository是基于PostgreSQL的特定实现,而toPO是一个转换器,用于将域对象PaymentAttempt转换为持久化对象。
基于领域模型设计的微服务
现在,我们已经为支付系统定义了一组有界上下文,并在每个有界上下文中标识了一组实体、聚合和领域事件服务。下一步是从域模型到应用程序微服务设计。这里,我们选择将一个有边界的上下文映射到一个微服务。
收益
采用DDD可以提供许多好处,例如,在所有团队之间进行清晰的沟通,以及在设计系统时使用成熟的模式来管理复杂性并提供更好的可伸缩性。
使用通用语言,我们可以实现更多的自描述类名和函数名。
使用聚合模式,我们可以实现清晰的边界和单一的职责。
使用领域事件模式,我们可以分割核心业务流程,减少聚合之间的耦合。
通过基础设施层和ACL模式,我们可以将核心业务领域模型与技术实现细节分离开来。通过限定上下文模式,我们可以派生出潜在的微服务候选对象。
挑战及教训
在实践中应用DDD时,我们想要分享一些挑战和经验教训:
- DDD是一个复杂模式的集合,需要整个团队花费很长时间来学习和理解,但是它会给您的系统来带设计好处。
- DDD没有为如何落地应用而提供一个框架。我们在本博客中提供的工作流程是我们的长期实践得出的最佳实践。
- 重要的是不要被标准答案所限制。有时,讨论一个模型名称或边界上下文定义可能会花费您大量的时间。
- DDD他是适合复杂的系统而不是适合所有的系统。
总结
- 在这个博客中,我们谈到了各种DDD的概念和策略,并提供了一个在Airwallex支付系统设计中应用DDD模式的全面工作流程。
- DDD模式是一个很大的主题(不可能涵盖全部细节),但是我们想介绍一些关键的主题和实践该模式的经验。
- 未来,我们将继续深入研究DDD模式中的各个主题,如怎么进行分层。领域事件存储、上下文映射模式等,以及如何在系统设计中应用它们。
原文地址:https://medium.com/@chaojie.xiao/domain-driven-design-practice-modeling-payments-system-f7bc5cf64bb3