切实有效的三个步骤:如何通过划分有界上下文设计微服务? - Robert Reppel

19-07-31 banq
                   

通过有界上下文和无所不在的语言,实现高聚合低关联并获得服务边界。

是什么让系统边界“干净整洁”?

我们通常使用的软件都是基于状态机的系统:像交通灯一样,changeLight()的结果取决于先前的状态是“红色”,“琥珀色”还是“绿色”。

当前状态取决于过去发生的事情(存储在诸如“数据库”和/或“事件日志”之类的事物中)。

任何此类机器的未来状态始终取决于:

  • 它目前的状态
  • 收到的状态更改指令(“命令”)。
  • 命令的参数。

如果无法保证将命令和状态封装在一起,那么就可能hack破解这个状态机,不通过命令直接修改当前状态,这也称为称为副作用。副作用会导致错误。它们可以导致主要的回归测试工作并减缓发布周期。他们甚至导致官僚主义。(“这次停机几乎杀死了我们。从现在开始,没有人在没有签字的情况下进行部署。”)

具有“漂亮和干净”系统边界的服务才具有凝聚力。将属于一起的东西在代码中放在一起,共享一个目标,不要将不属于一起的概念和功能混合在一起。

这样低耦合的服务才具有在其自己的数据存储和命令参数中做出决策所需的所有信息。它不会做出错误的决定或被拒绝,因为它不会依赖于调用另一个可能不可用或发生故障的外部服务。

让我们通过一个例子,通过使用业务领域事件和领域驱动设计的两个概念:“无处不在的统一语言”和“有界上下文”,我们如何构建更容易维护和更少副作用的免费软件:

第1步 - 沟通和理解:事件风暴

事件风暴是一个相当简单的想法:让人们进入一个房间并让他们通过一个系统描述各种工作流程:如果这一年内发生了什么,发生了什么业务活动(”订单已下单“,”付款已处理“, “物品运送”,......),将会发生什么,以什么顺序发生?

在这里,我们将使用自行车共享应用程序,该应用程序是在之前的博客文章中从实际的真实世界系统进行逆向工程。事件风暴同样可以很好地分享和理解新系统的需求以及探索现有的遗留代码。

传统上,事件风暴是通过墙上的便签贴完成的。而我们正在使用的是故事风暴StoryStream及基于文本的“事件风暴语言”(ESL)。StoryStream目前处于早期测试阶段。

事件风暴是一个强大的工具,因为它可以削减技术术语,并将参与者集中在业务成果上,用简单的英语描述,适用于建模服务和规划技术实施。

第2步 - 服务边界:将所属的组合在一起

忘记所有的技术考虑因素,查看领域事件,命令等,并将它们归类,这里介绍一个词语拖拉归类方法,可以通过Kubel来完成,Kubel是一个用于探索有界上下文的实验性开源工具。

将以下内容复制并粘贴到https://robertreppel.github.io/kubel/中

/// Bike Sharing
/// Locations
Set Up Bike Sharing Location-> : locationId, lat, long, name
Location Added
Locations* locationId, lat, long, name
Add Bike To Location-> bikeId, locationId
Bike Added : bikeId, locationId
// "Bike Added" could happen many times, depending on how many bikes are kept at this location.
Location* : list of bikes at location
/// Register User
Register-> email
Registration Started : userId, email
Pending Registrations*
Confirm Registration-> confirmationCode
Registration Confirmed : userId
Confirmed Registrations*
Set Password-> userId, password
Password Updated : passwordHash
Add Payment Method->
Payment Method Added : creditCardToken, last4Digits
My Profile* : userId, email, creditCardToken, last4Digits
Charge Deposit->
Payment Processed
Deposit Received
// Deposit received only occurs if payment processing succeeded.The "Charge Deposit" command may be (e.g.) issued by a process controller "microservice" which issues it as a result of having received "Registration Confirmed" and "Payment Method Added" events.
/// Ride
Unlock Bike-> locationId, bikeId
Bike Unlocked : bikeId
Ride Started : rideId, bikeId
Rides In Progress* :
Bike Removed : locationId, bikeId
Lock Bike-> bikeId, bikeLocationLat, bikeLocationLong
Bike Locked
Bike Returned
// Only mark bike as "returned" if it's within 10 meters of a location.
Bike Added: bikeId, locationId
/// User deletes account
Rides in Progress* : userId, bikeId, startLocationId, startTime
// Registration cannot be deleted if a user is currently on a ride
Delete Registration-> userId
Deposit Refunded : userId, amount, date
Registration Deleted
/// Remove bike sharing location
Remove Location-> locationId=123423
Bike Removed : locationId=123423, bikeId
Bike Added : locationId=123456, bikeId
Location Removed : locationId
// "Bike Removed" for each bike at the location which is being removed (123423), followed by "Bike Added" for to another location, e.g. 123456. Maybe one location (e.g. 123456) could be the depot or maintenance location?

点击“Generate Vocabulary”将文本输入转换为可拖动的词汇项。(中文也可以),将词汇表拖动到表示不同有界上下文的组中。每个这样的组都是作为单独服务实现的候选者。试试看。当我们这样做时,我们的结果:

这样我们有五种潜在服务:

  • 用户认证/授权
  • 乘客Rides
  • 地点
  • 交付过程
  • 存款

第3步 - 评估设计:Monkey Wrenching

我们有候选服务,但我们怎么知道他们是否有合适的微服务边界?关闭它们。

混沌工程通常用于测试实时IT系统的弹性,但在设计早期评估软件架构决策的业务影响时,这一技巧也很有用。做一个思想实验......

关闭位置服务:

  • 人们仍然可以注册,付款和存款,并查看自行车。
  • 在位置显示的可用自行车数量可能不正确,导致用户误导可用性。
  • 支持人员的自行车库存管理已不再可用。

关闭付款处理:

  • 仍然可以开始注册自行车共享服务并登录,但是检查自行车对新用户不起作用,因为不能进行损坏赔钱。
  • 我们设计付款处理是为了租用自行车过长,超期限,则现有用户仍可能在该项服务停止后还能checkou自行车。业务风险是:一些付款请求将失败,导致免费骑车和收入损失。这可能是不可接受的。
  • 用户仍然可以查看位置和可用的自行车数量。

不可用的服务会影响最终用户体验。设计时的“Monkey Wrenching”使得中断的影响明确,以便业务利益相关者可以进行必要的权衡。

某些影响是可以接受的,可以通过(例如)客户支持来处理。其他(例如上面的批量支付处理能力)需要技术缓解以及随之而来的资源和计划。

一个好的架构可以在服务失败时优雅地分解。是什么决定了可接受的“优雅”水准?是业务决策,而不是技术决策。

结论

我们已经看到了:“知道什么属于一起的自然能力”是如何用于建立服务边界,这使得松散耦合,内聚和弹性系统。该技术基于两个领域驱动设计概念:“无处不在的语言”和“有界上下文”。近年来,我们使用这种方法在实际项目中建立系统边界,并且运行良好。

使用语言而不是技术标准来确定系统边界的优势在于,在确定代码应该存在的位置时,它会产生自然,直观的“引导围栏guide fences”。

 

                   

2