写点什么

如何使用 DDD 方法验证业务规则

  • 2020-08-17
  • 本文字数:3236 字

    阅读完需:约 11 分钟

如何使用DDD方法验证业务规则

本文要点


  • 业务规则是系统拥有的特定领域知识的一个部分。

  • 合理构建的业务规则可以减少出错概率,并降低维护难度。

  • 不同业务规则的职责不同,例如提供验证或生成输出等。

  • 事件的处理方式决策会影响总体设计,并且在做出任何决策之前都需要了解其后果。

  • 应该围绕用户执行的任务来构造命令方法,并提供清晰的输出。


谈到业务规则,如果你是一名技术人员,可能首先想到的是代码行或业务规则引擎(如果你正在用的话)。但与编码相比,规则更接近知识管理的范畴。例如,建筑部门的员工了解许多关于卧室最小尺寸的规则,或者一片空间定义为卧室或书房的所需特征。如果一个人了解一个领域的规则,知道如何应用它们,就会成为这个领域的专家。我们的目标是创建模拟领域专家行为的软件应用程序。


在本文中,我将使用一个系统来管理公司的共享资源。这套系统的目标是帮助员工预订笔记本、投影仪、乒乓球桌等物品。


光是看上面一句话,你就可以立刻想出一串问题,比如:资源是什么?预订资源意味着什么?系统是否允许多次预订资源?诸如此类的问题应由领域专家回答,并将成为控制系统行为的业务规则。


这也将指导实体、值对象、命令和系统其他元素的定义。如何定义各个元素是更大的主题,超出了本文的范围。


也有专门针对这类主题的书籍,例如 Eric Evans 的《 领域驱动设计》;如果你在实现.NET,我建议你阅读《 使用.NET Core上手领域驱动设计》。


本文只会介绍如何构建业务规则这一部分,这些规则符合以下特征:


  1. 是一致的 。以任何开发人员都可以理解的方式组织规则对未来的更改是非常重要的。如果你认为系统总是在变化,而应用程序应该为这种变化做好准备,那么保持一致就是最佳选择。

  2. 很容易测试 。制定可测试的规则是确保更改不会破坏现有功能的唯一方法。

  3. 保持数据一致性 。保持数据一致是业务规则的主要职责之一。以我的个人经验来说,许多数据质量问题与系统规则处理不当有关。

  4. 很容易诊断 。我们应该努力避免任何意外的错误;但异常总是会发生的。如果你仅有的诊断信息说的是 对象引用为 null ,那么错误就非常严重了。


本文使用"输入-过程-输出"模型来表达系统规则。在这个模型中,一个输入被提供给系统;内部处理器接收输入并执行操作,从而创建输出。但是,在处理器开始执行任何操作之前,它必须验证所提供的信息是否有效。下一节将介绍如何处理这些验证。

模型细节


将命令发送到系统


使用基于域的方法处理验证的基本模型——这里显示了发送到系统的一条命令。


这个模型提供了在系统中处理业务规则的一个实现示例。这种方法基于领域驱动设计( DDD),但不用它而直接应用方法也是可以的。执行顺序如下:

1. 客户端发送一条命令

对于可以由客户端执行的建模命令,我们需要为其分配名称,从而识别它们。例如,它可以是 MakeReservation 之类的名字。注意,我们正在将这些设计定义移至软件设计和业务设计之间的一个中间点上。这听起来没什么特别的,但指定了名称之后,它可以帮助我们更有效地理解系统设计。这个想法是围绕任务而生的,与系统设计的 HCI(人机交互)概念有关。命令可帮助设计人员考虑系统需要支持的特定任务。


命令可能具有其他参数,例如日期、资源名称和用法说明。命令和查询责任隔离( CQRS)是一种使用命令对系统交互进行建模的模式。尽管在模型图解中客户端是一个用户,但它也可能是另一个系统。它还允许那些更改系统状态和仅返回数据的操作在系统的设计中具有不同的行为。



命令的语法验证

2. 语法规则验证

下一步是语法验证。根据 Merriam-Webster 所说,语法是将语言元素(例如单词)组合在一起以构成组分(例如短语或子句)的方式。在我们的上下文中,这意味着命令在领域的上下文中有意义。


这种验证可确保提供的数据对于系统而言是一致的。验证的一个示例是预订的开始日期——其值不应为过去的值,并且不能为 null。在验证阶段有多种返回错误的方法。Vladimir Khorikov 提供了一些有关 如何执行这些验证的示例。验证也可以在系统的不同层中进行。例如,最好在用户界面中通过验证确保提供的日期不是过去的,而不是在后端服务中发送这些值来做验证。这并不是说它也不能用在领域中;它可以防止其他客户端的滥用。



使用系统状态验证语义规则

3. 语义规则验证

语义验证取决于系统状态。此类规则旨在检查给定的命令在给定系统当前状态的情况下是否有意义,当前状态通常是通过系统实体存储和接收的。在 MakeReservation 命令之后,一种验证是检查是否有另一个客户保留了所请求的资源。这种验证也可以在客户端应用程序中进行;尽管如此,领域仍然有很多原因可以收到处理相同资源的预订请求。系统的责任是确保数据一致性。



处理更改系统状态的生产规则

4. 生产规则

生产规则是系统的核心。到目前为止,命令已经历了许多阶段,这些阶段可以确保系统能处理所提供的请求。生产规则指定了系统为达到所需状态而要执行的操作。它们处理的是客户试图完成的任务。它们使用 MakeReservation 命令作为参考,进行必要的更改以将请求的资源注册为保留资源。这些修改的结果是,实体和领域服务可以生成使客户端和其他系统知道更改已发生的事件。在我们一直使用的示例中,它将是 ResourceReserved。



事件的生成与处理

5. 事件

事件是一种与其他系统或子系统交互的通信机制。但是,在你的领域中实现它们之前,有必要确定系统对这些信号的反应方式。事件的处理有两种选项,各有其优缺点。


第一种是使用事务一致性。在这种模式中,系统使用同一事务来保持修改后的实体一致性。即使涉及多个汇总,事务也会跟踪更改并在流程结束时将其保存下来。这种模式称为工作单元。就代码生成而言,它提供了某些优势,例如简单性和效率。但有时它也是不切实际的。原因之一是它将修改后的实体保留在内存中,如果负载太大就会使用过多的内存和处理资源。另外,如果你在领域外通信,则将需要使用分布式事务,结果让应用程序更加复杂。


第二种选项是使用最终一致性。这将增强系统能力,因为你在执行命令时不会在内存中处理一个巨大的实体图。但这种选项会有其他后果。它意味着预订系统已完成其更改,并且如果其他子系统报告了故障,则预订系统必须运行一个补偿事务,还原先前的操作。这种方法还需要告知用户潜在的错误。选择某个选项时应权衡它们各自的优缺点、开发成本和所需的基础架构。例如,系统可能需要消息代理以同其他领域收发消息。



向客户端发送通知

6. 通知

命令完成后,系统必须将执行结果通知客户端。如果客户端是另一个系统,则可以省略此部分。但如果它是一个应用程序,我们可能需要通知用户命令的执行结果是成功还是失败,特别是在涉及异步处理时更需要这一步。继续讨论 MakeReservation 命令,其中一项要求是在分配特定资源之前需要所有者的批准。批准资源使用后,系统必须向用户发送确认信息。生成事件 ReservationApproved 时,模型中的通知开始运作。这个事件是另一条命令的结果,该命令应遵循我们之前描述的逻辑,但由不同的用户执行。


在设计中包含这一部分背后的想法是,除非获得批准,否则用户任务不会完成。同样,如果权限被拒绝,则领域可能需要还原部分更改。在设计系统时,我们必须考虑所有交互,直到用户的任务完成为止。

总结

业务规则是应用程序表达其对领域的了解的方式。正确构造业务规则有助于开发人员轻松理解代码,并应根据规则的职责将其分为不同类别。与此同时,使用结构化代码来表示用户任务可以减少开发团队需要理解的概念。因为业务规则取决于系统设计,所以解决方案架构师必须考虑设计支持它们的方式。


作者介绍:


Fabian Lopez 是佐治亚理工学院计算机科学硕士学位的软件工程师。目前,他在美洲开发银行担任解决方案架构师,设计烘焙行业的流程管理应用程序。Lopez 喜欢使用和创建软件模式,因为它为许多开发问题提供了解决方案参考。他定期参与用户研究,以识别用户模式并开发适合客户的解决方案。Lopez 注意到,许多好的技术解决方案失败了,因为用户在使用应用程序时陷入了迷茫,因而对解决方案的质量印象大减。


原文链接:


Using a DDD Approach for Validating Business Rules


2020-08-17 18:495467

评论

发布
暂无评论
发现更多内容

十步输出设计文档

鲁米

设计实践

配置引起事故复盘

风翱

3月日更

《Redis 核心技术与实战》学习笔记 04

escray

redis 学习 28天写作 3月日更 Redis 核心技术与实战

最近的一点思考「Day 28」

道伟

28天写作

寻找被遗忘的勇气(二十)

Changing Lin

3月日更

思考需要大声,写下便是永恒——未完待续

Justin

总结 28天写作

有利可图的NFT,NA公链(Nirvana Chain)NAC公链怎么面对高额Gas费的?

区块链第一资讯

区块链 公链 挖矿

永动金融EGG公链去中心化社交平台与通证EFTalk全球正式亮相

币圈那点事

区块链

国内可用镜像站整理

弘毅

基于SpringCloud,支持安卓、IOS、包含前后端等等完整网约车项目

Java架构追梦

Java 架构 面试 SpringCloud 网约车项目

2021年Java春招高级面试指南(1到5年Java面试者必备)

比伯

Java 编程 架构 面试 程序人生

C++线程池ThreadPoolExecutor实现原理

Linux服务器开发

c++ 后端 线程池 Linux服务器开发 Linux后台开发

Python 随机数函数 choices

HoneyMoose

Go语言学习笔记:数组

worry

数组 Go 语言

这份1307页Android面试全套真题解析,源码+原理+手写框架

欢喜学安卓

android 程序员 面试 移动开发

这些面试题你会吗?6年菜鸟开发面试字节跳动安卓研发岗,复习指南

欢喜学安卓

android 程序员 面试 移动开发

翻译:《实用的Python编程》07_00_Overview

codists

Python

老板要我开发一个简单的工作流引擎

Java小咖秀

架构 工作流 开发 工作流调度 工作流引擎

【LeetCode】逆波兰表达式求值Java题解

Albert

算法 LeetCode 28天写作 3月日更

多队列网卡简介

依旧廖凯

28天写作 3月日更

Python 随机字符串

HoneyMoose

2020年张小龙非公开课的一点思考

lenka

3月日更

TCP 三次握手与四次挥手

insight

TCP 3月日更

我在阿里实习做开源

apache/dubbo-go

微服务 程序人生 云原生 dubbo dubbogo

蚂蚁二面:MQ消费端遇到瓶颈除了横向扩容外还有其他解决办法?

中间件兴趣圈

面试 RocketMQ 消息中间件

28天

ES_her0

28天写作 3月日更

Wireshark数据包分析学习笔记Day16

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

Python 分通道读取图像数据,取经之路第 4 天

梦想橡皮擦

28天写作 3月日更

翻译:《实用的Python编程》07_01_Variable_arguments

codists

Python

打通Jira与钉钉和企业微信不再难

跟YY哥学Jira

钉钉 Jira 企业微信 automation

MySQL如何选择主键

架构精进之路

MySQL 3月日更

如何使用DDD方法验证业务规则_研发效能_Fabian Lopez_InfoQ精选文章