写点什么

条件型业务规则的抽象与实现——从 Spring Profile 得到的灵感

  • 2020-04-06
  • 本文字数:3336 字

    阅读完需:约 11 分钟

条件型业务规则的抽象与实现——从Spring Profile得到的灵感

摘要

当我们更倾向于使用具体的场景沟通的时候,团队更不容易意识到需要从中寻找稳定的抽象。那么我们需要花费精力去改变用户的思维方式吗,如果需要又应该使用什么样的方式?又或者我们需要使用更抽象的方式来撰写用户故事吗?


最近,有幸参与了一个平台型的项目,该平台支持多种类型的产品预订,并且对于不同的产品类型,支持不同的预订规则。开发团队想尽可能地将主流程实现得更通用,以便在将来更快速地支持新的产品类型。因此,团队决定在主流程中,以产品类型作为条件,决定是否应用某个给定的预订规则。


例如其中有一个对于配送地址的验证规则,它只对特定产品类型(火车票)生效:


(经过简化的用户故事——火车票预订)

作为用户,当我预订火车票时,我应该被告知配送地址无法送达,以便我调整配送地址或选择上门取票


该平台还支持预订酒店,不过由于没有凭据需要配送,所以并不需要检查配送地址是否可达。于是有了以下实现:


public class AddressIsAvailableToDelivery implements PlaceOrderRule {
@Override public void verify(PlaceOrderCommand command) { if (command.getProduct().isTypeOf(RAILWAY)) { // check if the adress is available for delivery the ticket } else { // hotel, makes no sense of deliering tickets } }}
复制代码


预订主流程会依次执行所有的PlaceOrderRule,并由各个PlaceOrderRule的实现决定需要对哪些产品生效。


几个迭代过后有了新的产品需要支持:观光景点,需要配送门票给用户,所以一个类似的用户故事诞生了:


(经过简化的用户故事——门票预订)

作为用户,当我预订景点门票时,我应该被告知配送地址无法送达,以便我调整配送地址或选择上门取票


于是,团队修改了条件表达式,增加了对门票景点的判断:


public class AddressIsAvailableToDelivery implements PlaceOrderRule {
@Override public void verify(PlaceOrderCommand command) { if (command.getProduct().isTypeOf(RAILWAY) || command.getProduct().isTypeOf(SIGHTSEEING)) { // check if the adress is available for delivery the ticket } else { // hotel, makes no sense of deliering tickets } }}
复制代码


到这里,我们闻到到了一些”坏味道”:随着需要验证地址是否达的产品类型增加,代码的圈复杂度会随之升高,意味着需要更多的测试用例来保护。如果将来再有一个新的类型需要检查配送地址是否可达,可以预见此处还会修改;如果系统中有越来越多的条件型业务规则使用当前的方式实现,系统将会越来越脆弱。

找到稳定的抽象

那么问题出在哪里?我认为这是由于没有找到正确的抽象,对于条件型的业务规则,其实是有稳定的步骤的:


  1. 检测当前情况是否需要验证给定的业务规则

  2. 如需要,执行验证;如不需要则略过


如果将AddressIsAvailableToDelivery修改为:


public class AddressIsAvailableToDelivery implements PlaceOrderRule {
@Override public void verify(PlaceOrderCommand command) { if (command.getProduct().isDeliverableAddressRequired()) { // check if the adress is available for delivery the ticket } else { // hotel, makes no sense of deliering tickets } }}
复制代码


这样,条件表达式依赖了稳定的抽象。代码不需要再关心产品类型了,当新的产品加入平台时,只需要知道该产品是否需要验证配送地址就行了。这样就做到了当新产品加入时,核心的规则验证逻辑不需要变更,系统更加稳定。

但这样好难用

工程师对这个重构感到满意,于是找到了 BA(业务分析师),尝试对用户故事做一些变化


(经过简化的用户故事——产品预订)

  1. 作为用户,当我预订需要检查配送地址是否可达的产品时,我应该被告知配送地址无法送达,以便我调整配送地址或选择上门取票

  2. 作为运营人员,我可以设置产品在预订时是否需要检查配送地址,以避免预订后无法配送凭证的情况


BA 对此提出了担心:


  1. 在这个实现方案中,平台运营团队需要为不同的产品设置不同的规则吗?如果规则数量很多,配置起来是不是很麻烦?因为对于某个产品类型,几乎不需要做规则的调整,要求运营团队去配置这些功能在现阶段反而使他们的工作变复杂了

  2. 平台运营团队在平时的工作中,还是按照产品类型的思维在工作的,他们更习惯于”如果产品类型是火车,那么。。。”这样的沟通方式,想要改变这样的思维方式不是那么容易

  3. 修改后的用户故事似乎太抽象了,这样能否帮助团队有效地理解真实的业务场景?


当有大量规则的时候,细粒度的产品配置方式确实有些繁琐,可能需要“配置专家”才能搞定。



(大量规则的时候,细粒度的产品配置方式可能需要”配置专家”才能搞定)


这些担忧不无道理,团队一下子陷入了两难的境地。

意外的灵感

我在阅读该项目一段配置代码的时候发现了这样一个细节:


if (isSmsEnabled()) {   //enable sms sending}
if (isEmailEnabled()) { //enable email sending}


// application.propertiessms.enabled: falseemail.enabled: false
// application-dev.propertiessms.enabled: falseemail.enabled: false
// application-qa.propertiessms.enabled: false email.enabled: true
// application-prod.propertiessms.enabled: true email.enabled: true
复制代码


这段代码表示,在不同的环境中,通过细粒度的配置项,可以精确地控制某个特定功能是否起效。配置项的控制范围很小,而且可能会有许多这样的配置项,但团队根据各个环境上的测试约定,将这些配置项归拢到以环境命名的配置文件中,这是spring boot提供的Profile机制。在启动应用的时候,并不需要一一指定各个配置项的值,而是指定粗粒度的profile即可: --spring.profiles.active=prod


这个方案给了我一个灵感:能否将之前的预订规则表达式类比为配置项,产品类型类比为Profile呢?


在这个思路下,我们保持AddressIsAvailableToDelivery依赖稳定的isDeliverableAddressRequired


public class AddressIsAvailableToDelivery implements PlaceOrderRule {
@Override public void verify(PlaceOrderCommand command) { if (command.getProduct().isDeliverableAddressRequired()) { // check if the adress is available for delivery the ticket } else { // hotel, makes no sense of deliering tickets } }}
复制代码


而在实例化Product时,注入预先设置的配置项,将产品类型和配置项的转换从核心的规则校验中剥离出去。


# railwayplaceOrderRule.RAILWAY.deliverableAddressRequired=trueplaceOrderRule.RAILWAY.anotherConstraint1=falseplaceOrderRule.RAILWAY.anotherConstraint2=false# sightseeingplaceOrderRule.SIGHTSEEING.deliverableAddressRequired=trueplaceOrderRule.SIGHTSEEING.anotherConstraint1=falseplaceOrderRule.SIGHTSEEING.anotherConstraint2=true
复制代码


这样,既能让核心的规则校验依赖稳定的抽象,在变化时保持结构稳定,又暂时避免了给运营团队带来繁琐的配置工作。

遗留的问题

回顾这个过程,实在有些偶然,而且我认为我们只是用了最熟悉的技术手段暂时缓解了之前 BA 提出的第一点担心。


  1. 平台运营团队在平时的工作中,还是按照产品类型的思维在工作的,他们更习惯于”如果产品类型是火车,那么。。。”这样的沟通方式,想要改变这样的思维方式不是那么容易。

  2. 修改后的用户故事感觉太抽象了,这样能否帮助团队有效地理解真实的业务场景?


而 2、3 则涉及到项目团队和干系人对产品的思考方式,当我们更倾向于使用具体的场景沟通的时候,团队更不容易意识到需要从中寻找稳定的抽象。那么我们需要花费精力去改变用户的思维方式吗,如果需要又应该使用什么样的方式?又或者我们需要使用更抽象的方式来撰写用户故事吗?在这里,想听听大家的意见。


作者介绍


周宇刚,拥有 10 年的 JAVA EE 开发经验,在 ThoughtWorks 担任高级咨询师。在加入 ThoughtWorks 之前,在一家国内领先的航旅企业担任架构师,专注于持续交付实践和大型企业应用架构治理。


本文转载自 ThoughtWorks 洞见。


原文链接


https://insights.thoughtworks.cn/identity-rule-abstraction-implementation/


2020-04-06 10:151950

评论 4 条评论

发布
用户头像
配置全放在配置文件里,这就导致了非常依赖于开发人员,跟配置专家应该没有两样,并没有本质上减少什么。
而且在微服务环境下,大多数项目都集成了类似applo、nacos等配置中心。
2020-04-07 11:46
回复
用户头像
请问作者,为什么不把配置存在数据库呢?感觉这种业务细节的配置放在配置文件不太舒服。如果不想让运营团队设置配置,可以不开放相关后台管理入口呀。
2020-04-07 07:55
回复
存数据库,在高并发下应该不是一种好的方案
2020-04-07 08:31
回复
这种配置 更多像是一种开机启动的配置。并不会随时被改变。
2020-04-07 11:47
回复
没有更多了
发现更多内容

免费下载 | 2023 中国技术成熟度评估曲线发布,共看六大发展趋势

博睿数据

可观测性 智能运维 博睿数据 权威报告

maven pom 中有用的两个配置

JefferLiu

WeOps上新啦 | WeOpsV3.14拓展云平台能力,支持自动发现和监控告警

嘉为蓝鲸

自动化运维 weops 嘉为蓝鲸

小场景解决大问题|明道云在京东方的落地实践

明道云

Svelte框架实现表格协同文档

葡萄城技术团队

昆仑万维深耕AIGC领域 昆仑天工助力内容创作者创造无限可能

Geek_2d6073

​洞悉获客之道,林肯汽车开展高端社区精准营销俘获消费者芳心

联营汇聚

标准升级 |《企业数字化成熟度模型IOMM标准》(企业整体视角)发布

信通院IOMM数字化转型团队

数字化转型 IOMM ICT深度观察

嘉为蓝鲸研运一体化解决方案荣获信通院XOps领域年度明星解决方案

嘉为蓝鲸

自动化运维 嘉为蓝鲸

SQL 嵌套 N 层太长太难写怎么办?

王磊

如何对小程序进行更高效的管理

Onegun

小程序 微信小程序 小程序管理平台

揭开华为云CodeArts TestPlan启发式测试设计神秘面纱!

华为云开发者联盟

云计算 后端 华为云 企业号 1 月 PK 榜

聚焦技术与体验极致提升,阿里云视频云连续5年领跑!

阿里云视频云

阿里云 IDC 视频云

MASA Stack 1.0 发布会圆满收官

MASA技术团队

.net 云原生 PaaS dapr MASA

阿里云云边一体容器架构创新论文被云计算顶会 ACM SoCC 录用

阿里巴巴云原生

阿里云 容器 云原生

如何利用极狐GitLab 轻松管理NPM依赖发布与更新?

极狐GitLab

node.js DevOps npm 依赖 极狐GitLab

如何通过C#和VB.NET合并Excel文档

Geek_249eec

C# Excel VB.NET

KubeVela 再升级:交付管理一体化的云原生应用平台

阿里巴巴云原生

阿里云 开源 云原生 KubeVela

位运算在数据库中的实际应用

领创集团Advance Intelligence Group

数据库 位计算

21世纪啤酒与尿布的故事

Marvin Ma

广告 流媒体 啤酒与尿布

2022Q4消费级AR眼镜市场季度分析:雷鸟创新领跑,华为强势入局

易观分析

市场 消费 AR眼镜

深度 | 新兴软件研发范式崛起,云计算全面走向 Serverless 化

阿里巴巴云原生

阿里云 Serverless 云原生

还在用Excel和SQL?火山引擎VeDI这款产品帮你更快处理数据

字节跳动数据平台

大数据 数据分析 企业 数据看板

英特尔CEO帕特·基辛格:五大“超级技术力量”,推动人类社会发展

科技之家

叮咚~,这份春节前突击检查应对指南请收好!

嘉为蓝鲸

自动化运维 weops 嘉为蓝鲸

干货 | 企业监控系统体系化建设思路

嘉为蓝鲸

自动化运维 嘉为蓝鲸 企业监控系统

网易伏羲预训练模型”玉言“登顶CLUE分类榜单,成绩首次超过人类水平

网易伏羲

人工智能

Kyligence 成功签约阿斯利康,助力提升商业洞察质量和决策效率

Kyligence

数据分析 指标中台

什么是AutodeskMaya?为什么要学习它?

Finovy Cloud

3DMAX maya

CuPL 利用大规模的语言模型,更高效地生成提示

Zilliz

安卓影像飞升时刻:vivo X90 Pro+打通HDR任督二脉

脑极体

Vivo 蔡司影像

条件型业务规则的抽象与实现——从Spring Profile得到的灵感_软件工程_张凯峰_InfoQ精选文章