zoukankan      html  css  js  c++  java
  • 架构层级的“开闭原则”

    本问是关于架构层级SOLID原则的文章,在类的层级,开闭原则(the-Open-Closed-Principle,简称OCP原则)的含义是:一个类对扩展是“开”放的,而对变更是封“闭”的,意思是说,应该在不改变类的前提下扩展一个类的行为。而通常的方式是继承和多态。

    在架构层级,我们并不会变更系统的一部分功能(可能是最适用于当前架构的进程,守护进程,服务,或者微服务),而是通过新增功能的方式来复用已完成的代码。为了不对现有的部分做出变更,系统需要做到完全的解耦。接下来的内容将聚焦于事件驱动系统,并以消息队列实现服务间通信。消息队列 可以是ActiveMQ, RabbitMQ, ZeroMQ, Kafka或者其他服务,我将以Kafka的话语体系来进行描述,如主题(Topic),发布者,订阅者,以及类似Kafka的多个订阅者共享相同主题的能力。

    举一个具体的例子。假设我们在一家汽车租赁公司工作,并负责建立一个车辆的可用性系统。整个租赁流程的步骤如下:

    第1步,车辆租赁:包含租赁协议的签订和客户选车的过程。随即可用的车辆数减1。 

    第2步,客户用车:客户在一定的时间范围内使用租赁的车辆。

    第3步,车辆归还:车辆的归还和签到。随即可用的车辆数加1。

    其中第1步和第3步都需要将租赁协议入库,因此我们可以设计一个事件,RentalAgreementSaved,在保存数据时触发。这一事件将被存储在RentalAgreementSaved主题中。因此到目前为止,共有两个发布者向主题发送消息,一个是CarRental微,另一个是CarCheckin微服务。

     

    下面来定义消息的内容。鉴于本主题的意图是为了表征租赁协议的保存,因此所需的最小信息量即协议ID。但系统的使命是跟踪车辆的可用性,最好还是设置一个Status字段。这一字段可以有两个值:

    激活状态。代表客户正在使用车辆。

    关闭状态。代表客户已经归还了车辆并进行了签到。

    CarRental微服务可以选用如下的JSON数据结构:

    {"Status":"Active","RentalAgreementID":1234}

    CarCheckin微服务对应的数据结构是:

    {"Status":"Closed","RentalAgreementID":1234}

    Status字段本应从数据库读取,并通过协议ID加载租赁协议,但因为我们只关心车辆的可用性,直接在JSON消息中写入协议ID更简单,性能也更好。这一点在后文还会提到。

    有了消息发布者和消息的格式,我们就可以完成下面的图表:

     

    CarAvailability微服务会对发送给RentalAgreementSaved主题的消息进行消费:如果Status是“激活状态”,就减1,如果Status是“关闭状态”,就加1。

    我们扩展了系统的功能,但并没有变更系统的代码。只是利用了多个订阅者可以订阅同一个消息主题的机制。因此是的,OCP原则可以在架构层级得以应用。

    迪米特法则

    迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简称LKP),就是说一个对象应当对其他对象有尽可能少的了解。

    假设,我们对现有的功能很满意,并准备添加一个新的功能:向用户发出感谢信来感谢他或她使用了我们的服务。参考发票微服务的例子,我们可以同样从数据库中获得租赁协议和用户数据。但这样的设计效率不高,因为CustomerThanking服务根本不需要用到租赁协议的内容。事实上,这也违反了“迪米特法则”,而我们希望所有的系统都是符合良好的架构实现的。

    变更消息内容违背了OCP原则,

    还好,确实有其他方案可供选择。我们可以让领域驱动设计(DDD)来帮忙。只要将领域拆分成有界上下文,就可以利用其优势来完成工作。在一个超简化的系统模型中,我们可以定义如下的有界上下文:

    租赁协议 客户  车辆。

    租赁专员:使用系统来办理租赁协议 租赁中介:通常情况下客户并不直接租车,而是通过代理人来租车

    所有这些实体都出现在租赁协议中,但又自成有界上下文。因此,在最初设定消息格式的时候,我们可以根据正在进行的操作来引入主要的有界上下文。

    在本例中,初始的消息内容设计应该是下面的样子:

    {"Status":"Closed","RentalAgreementID":1234,"CustomerID":8965,"VehicleID":98263,"RentalAgent":24352,"Broker":6723}

    有了这样的消息结构,我们总算可以在不打破OCP原则和“迪米特法则”的情况下实现CustomerThanking微服务了。

    原文:http://t.cn/E6FVsm6

  • 相关阅读:
    Django关于StreamingHttpResponse与FileResponse响应文件或视频的下载请求
    APScheduler可能遇到的问题
    django中model聚合使用
    Java 递归判断迷宫问题是否有路
    direct path read/write (直接路径读/写)
    DRM 简介
    SQL Server2008表名中含“.”号处理方法
    Java学习之:JDK动态代理与CGLIB动态代理
    强大易用!新一代爬虫利器 Playwright
    为什么cudaMalloc()参数是二级指针
  • 原文地址:https://www.cnblogs.com/wmy-666/p/11030875.html
Copyright © 2011-2022 走看看