草庐IT

微服务架构 | 如果是你,你会怎么拆?

桑小榆 2023-03-28 原文

 

hi

前面我们一起探讨了一个微服务的概念了解,微服务,也称为微服务架构,是一种架构风格,它将应用程序构建为服务的集合。

那么,假如你所负责的应用变得庞大,需要进行重构,我们采用微服务架构,对你现在的需求进行拆分设计,你会怎么拆分呢?

/ 

 

务拆分策略

首先,我们需要考虑的是共享类库的角色,也就是我们在开发过程中,习惯把一些通用的功能(帮助类)打包成一个公共类库或者包中。其他服务需要使用直接引用调用即可,增加代码的复用性。但是很可能存在隐患,这个公共类库不断地叠加导致出错或者版本问题会影响到其他服务。

更好的做法就是努力使。或者是

其次,在拆分服务时,我们需要精心设计的服务将适合由一个小团队开发的服务,并且交付时间最短,与其他团队的协作最少。同时,将一些小的、松耦合的服务组织到一起,以便提升开发阶段效率,特别是可维护性、可测试性和可部署性,这样能够让组织的软件开发速度更快。

接下来,我们需要对一个应用程序来定义微服务架构,进行拆分了。

第一步通常在开始之前,我们需要收集这个应用有关的信息,比如说需求文档,领域专家的信息(具有这个领域的丰富知识和技能)等。之后,我们便需要从这些信息中提炼出各种关键信息,使用抽象的系统操作来描述服务之间协作方式的架构场景,这是一种抽象化的,而不是具体的,它既可以是更新数据的命令,也可以是检索数据的查询。每个命令的行为都是根据抽象的领域模型定义的,抽象领域模型也是从需求派生出来的。

第二步,就是如何分解服务。这里有两种广为熟知的策略,这两种方式最终都是围绕业务概念而非技术概念分解和设计的服务,所以最后的设计结果往往会有些相像。

第三步,即是确定每个服务的API。这一步设计过程中需要采用哪种进程间通讯机制来实现每个服务API的通讯。同时,需要考虑几个困难,第一个就是,如果服务之间往返太多,导致网络延迟,那么你需要重新审视你拆分的服务是否合理。第二个就是。第三个就是需要维护跨服务的数据一致性,例如使用Saga,共享数据库,领域事件来解决,当然这些将会在后续探讨。第四个,就是,上帝类即是整个应用中的全局类,可以通过驱动领域的概念来消除这个上帝类。

细心的伙伴会发现,在采用微服务架构时,很多难啃的骨头都可以通过DDD(驱动领域)来化解,这也是广为使用的原因之一,能够很好的配合微服务架构。

这里我们展开讲解第二步,如何分解服务。第三步将会涉及更多内容,将在后续逐一探讨。

使用业务能力进行服务拆分,是微服务架构的策略之一。或许不用讲也知道,业务能力是指一些能够为公司(或组织)产生价值的商业活动。每个企业也有自己的特定的业务类型。例如,在线商店所包含的业务能力包括:订单管理、库存管理和发货。保险公司业务能力包含承保、理赔管理、账务和合规等。

公司的业务能力通常是指这个组织的业务是,它们通常是稳定的。与之相反公司采用何种方式来实现它的业务能力,是随着时间不断变化的。例如以往的支付方式,除了线下支付,还支持财付通支付,现如今还支持支付宝,微信支付。但是始终属于支付业务,稳定不变的,仅仅只是实现方式发生了变坏。

/ 

 

业务能力的拆解,我们可以举一个例子:例如一个餐厅管理系统,其业务能力包含:

  1. 供应商管理。送餐员相关信息,餐馆菜单和其他信息管理,例如营业时间和地址。

     

  2. 消费者管理:消费者相关信息的管理。

     

  3. 订单获取和履行(直接运送、第三方派送,自己运送等)。具备消费者创建和管理订单,餐馆管理订单生产过程,送餐,跟踪外卖员的实时状态,订单送到用户手中。

     

  4. 会计记账。管理跟消费者相关的会计记账,管理跟餐馆相关的会计记账,管理外卖员相关的会计记账。

     

  5. 其他。

     

通过上面的业务梳理,我们可以对应到API服务。

 

 

Eric Evans 于 2003 提出的领域驱动设计构建复杂软件的方法论,这些软件通常都以。领域模型以解决具体问题的方式包含了一个领域内的知识。它定义了当前领域相关团队的词汇表,DDD也称之为通用语言。领域模型会被紧密地映射到应用的设计和实现环节。在微服务架构的设计层面,DDD有两个特别重要的概念,子域和限定上下文。

DDD例如上例某餐馆服务的子域包括:订单获取、订单管理、餐馆管理、送餐和会计。能够看出和上面业务能力拆分服务非常相近。

DDD限界上下文包括实现这个模型的代码集合。当使用微服务架构时,每一个限定上下文对应一个或者一组服务。换一种说法,我们可以通过DDD的方式来定义子域,并把子域对应为每一个服务,这样就完成了微服务的设计工作。

或许这些内容需要多读多理解,下面我们使用一个小案例来助解。

假如我们要为一个小卖部设计一套进销存系统,她为我们提供的业务描述是这样的:每天凌晨从布吉农批市场买苹果、梨、葡萄、橘子、香蕉、荔枝、核桃等等,反正哪些好卖她就买回来卖。葡萄、荔枝不能长久保留,一般要当天卖出去…。

针对上面这段业务描述,我们怎么进行领域模型设计?将给出以下几个步骤来完成领域模型设计。

总结业务描述中的名词。首先建一个名词表,把涉及到的名词列出来:

1. 布吉农批市场

2. 买东西的人是一个隐含的名词,每天凌晨从农批市场拿货

3. 苹果

4. 梨

5. 葡萄

6. 橘子

7. 香蕉

8. 荔枝

9. 核桃

10. 顾客是一个隐含的名词,买回来卖的对象

11. 凌晨、当天时间名词,与实体及角色无关

 

 

确定业务实体,用序号名词描述;

1. 布吉农批市场不是本业务的一个实体

2. 买东西的人是本业务的一个角色

3. 苹果是一个实体

4. 梨是一个实体

5. 葡萄是一个实体

6.橘子是一个实体

7. 香蕉是一个实体

8. 荔枝是一个实体

9. 核桃是一个实体

10. 顾客是本业务的一个角色

11. 凌晨、当天时间名词,与实体及角色无关

 

 

?苹果、梨、葡萄、橘子、香蕉、荔枝属于水果,核桃属于干果,它们都是果品的一个具体实例。而在水果中葡萄和荔枝属于不宜保存水果,通过这样进一步的分析得出如下的领域模型:

果品进销存领域模型

这个领域模型不但能反映当前的经营实体,同时给我们需求分析人员和系统功能提供了一定的扩展视野:将来会不会经营食品,短期保持水果采取什么利润空间来促销,长期保存的水果会不会因为保存成本而导致利润下降。

那么,我们根据领域模型对业务能力拆分的某餐馆系统进行设计如下:

DDDDDD

 

?

 

 

 

 

有关微服务架构 | 如果是你,你会怎么拆?的更多相关文章

  1. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  2. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  3. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  4. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

  5. ruby - Ruby 中的隐式返回值是怎么回事? - 2

    所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

  6. ruby - 怎么来的(a_method || :other) returns :other only when assigning to a var called a_method? - 2

    给定以下方法:defsome_method:valueend以下语句按我的预期工作:some_method||:other#=>:valuex=some_method||:other#=>:value但是下面语句的行为让我感到困惑:some_method=some_method||:other#=>:other它按预期创建了一个名为some_method的局部变量,随后对some_method的调用返回该局部变量的值。但为什么它分配:other而不是:value呢?我知道这可能不是一件明智的事情,并且可以看出它可能有多么模棱两可,但我认为应该在考虑作业之前评估作业的右侧...我已经在R

  7. ruby-on-rails - 我该怎么办 :remote location validation with CarrierWave? - 2

    我在我的Rails3示例应用程序上使用CarrierWave。我想验证远程位置上传,因此当用户提交无效URL(空白或非图像)时,我不会收到标准错误异常:CarrierWave::DownloadErrorinImageController#createtryingtodownloadafilewhichisnotservedoverHTTP这是我的模型:classPaintingtrue,:length=>{:minimum=>5,:maximum=>100}validates:image,:presence=>trueend这是我的Controller:classPaintingsC

  8. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. ruby - EventMachine - 你怎么知道你是否落后了? - 2

    我正在研究使用EventMachine支持的twitter-streamruby​​gem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@

随机推荐