• 微服务和领域驱动


    一、微服务

    1.1 什么是微服务

    微服务就是一些协同工作的小而自治的服务。

    关键词: 小而自治

    --

    小”这个概念,一方面体现在微服务的内聚性上。

    • 内聚性也可以称之为单一职责原则:“把因相同原因而变化的东西聚合到一起,而把因不同原因而变化的东西分离开来。
    • 也就是说,微服务应该专注于做好一件事情。
    • 由业务边界来确定服务的边界

    -- 自治

    自治”这个概念,强调的是,一个微服务就是一个独立的实体。体现在服务之间的松耦合上。黄金法则:你是否能够修改一个服务并对其进行部署,而不影响其他任何服务?

    1.2 微服务的好处

    • 技术异构性
      • 不同服务根据业务场景、性能要求、功能需求采用不同的技术架构
      • 新技术的快速实践,技术团队的快速成长
    • 弹性/反脆弱性(anti-fragility)
      • 服务降级
      • 服务容灾、服务熔断
    • 扩展
      • 传统单体系统,无法做到对局部功能进行扩展
      • 根据具体的业务需求,对特定的微服务进行扩展
      • 通过架构的手段,节省成本
    • 简化部署
      • 传统单体应用,即使是一行代码修改,也需要整体重新部署。风险太大。
      • 微服务架构,各个服务部署相互独立
      • 灵活的发版方式
      • 快速回滚
      • 风险小
    • 架构与组织结构相匹配
      • 关联知识点:康威定律
      • 服务的可重用、可组合
      • 服务的可替代性,快速重写

    1. 3微服务的弊端

    对运维能力要求高;运行效率会降低;技术要求高,需要处理事务最终一致性等问题。微服务不该是这样的:

    二、好服务的特征

    2.1松耦合

    松耦合指的是,修改一个服务,而另一个相关的服务,不需要修改。微服务的重要特点就是:能够独立修改和部署单个服务,而不会影响、甚至修改系统中的其它服务。

    一个服务应该尽可能少地了解与之协作的服务信息。因为过度了解,会导致紧耦合。

    2.2高内聚

    高内聚指的是,把相关的行为聚集在一起。这样,需要改变行为时,只需要在一处修改,然后独立发布。

    我们需要做的是,找出业务领域的边界,然后实现高内聚

    三、微服务拆分原则

    还是要重申两个重要概念,高内聚和松耦合,对应前面的关键词:小而自治

    3.1限界上下文

    微服务一定要有清晰的功能边界。一个微服务对应了一个功能集合,这些功能一定是有一些共性的。比如,订单服务,那么创建订单、修改订单、查询订单列表,一般是订单服务的功能集合。

    3.2逐步划分

    一开始,我们会首先识别出一个粒度比较粗的服务模型。领域拆分并不是一步到位的,应当根据实际情况逐步展开。从单体应用到微服务体系的拆分过程能很好的说明这个问题。所以如果一开始不知道应该划分多细,完全可以先粗粒度划分,然后随着需要,初步拆分。比如一个电商一开始索性可以拆分为商品服务和交易服务,一个负责展示商品,一个负责购买支付。随后随着交易服务越来越复杂,就可以逐步的拆分成订单服务和支付服务。

    四、DDD领域驱动设计

    DDD 是一种架构设计方法,微服务是一种架构风格,DDD可以看作是微服务的方法论。两者从本质上都是为了追求高响应力,而从业务视角去分离应用系统建设复杂度的手段。两者都强调从业务出发,其核心要义是强调根据业务发展,合理划分领域边界,持续调整现有架构,优化现有代码,以保持架构和代码的生命力,也就是我们常说的演进式架构。

    • DDD是一个很好的应用于微服务架构的方法论;
    • 在项目的全生命周期内,所有岗位的人员都基于对业务的相同的理解来开展工作。所有人员站在用户的角度、业务的角度去思考问题,而不是站在技术的角度去思考问题;
    • 诞生于2004年,兴起于2014年(微服务元年);
    • DDD晦涩难懂,难以落地。因为DDD是方法论,不是行动指南

    4.1 领域和子域

    领域可以是多个子领域的一个虚拟集合,换句话说多个微服务也可以形成一个大域,不必纠结于领域和微服务之间的数量对应关系。例如在做架构设计的时候可能就把订单域作为一个领域,代表了这个域就是关于订单的,具体该有几个微服务,这需要更细的详细设计来提供。子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域

    4.2 界限上下文

    任何领域模型都必须是在其特定的上下文环境中,如果DDD超出了其界定的上下文,则会产生歧义或者错误。例如社交媒体中的连接和网络应用中的连接的含义是完全不同的。对于任何上下文,我们都必须要能够建立明确的上下文的关联关系和边界。模型中的角色和边界的定义,在项目中非常重要。

    4.3 

    库(Repositories)库指的是所有拥有共同实体和值接口的服务,且这些实体和值都在同一个聚合组中。库通常应该有增加,删除,修改和查找组中对象的方法。可以直接通过应用业务逻辑查询的引用来进行简洁的查询。

    4.4 通用语言

    通用语言就是能够简单、清晰、准确描述业务涵义和规则的语言。通用语言是团队统一的语言,不管你在团队中承担什么角色,在同一个领域的软件生命周期里都使用统一的语言进行交流。那么,通用语言的价值也就很明了,它可以解决交流障碍这个问题

    4.5 聚合和聚合根

    聚合(Aggregate):用来定义领域对象所有权和边界的领域模式,是用来帮助简化模型对象间的关系。通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系网的形成。

    聚合根(Aggregate Root):每个聚合都有一个根对象(聚合根实体),从外部访问只能通过这个对象。根实体对象有组成聚合所有对象的引用,但是外部对象只能引用根对象实体。只有聚合根才能使用仓储库直接查询,如果根实体被删除,聚合内部的其它对象也将被删除。

    4.6 实体和值对象

    实体(Entity):一个由它的标识定义的对象叫做实体。通常实体具备唯一ID,能够被持久化,具有业务逻辑,对应现实世界业务对象。

    值对象(Value Object):一个没有概念上标识符描述一个领域方面的对象,这些对象是用来表示临时的事物,或者可以认为值对象是实体的属性,这些属性没有特性标识但同时表达了领域中某类含义的概念。通常值对象不具有唯一ID,由对象的属性描述,可以用来传递参数或对实体进行补充描述。

    4.7 领域事件

    领域事件是对领域内发生的活动进行的建模。一个消息监听可以当成一个领域事件,表示发生了什么事情,然后需要做什么。领域事件对象用于记录模型产生的离散事件,我们必须谨慎选择和记录领域事件,仅仅对于那些有意义的事件进行记录。

    4.8 领域和数据库

    一般按垂直业务拆分,一个服务对应独立的库(也可能出现多个服务公用一个库,从设计上通常没有公用表),绝大多数情况下,一个服务和一个数据库搭配,对外提供接口。数据库应该被视为每个领域服务层微服务的私有数据库。

    4.9 领域内拆分

    领域拆分并不是一成不变的,应当具体情况具体分析。比如大众点评,其订单服务就拆分为了order-service和order-query-service,一来为了读写分离,二来order-query-service作为单独应用可以按需水平扩容。

    4.10 核心子域、支撑子域、通用子域

    -- 核心子域:

    决定业务成功和公司核心竞争力的子域,整个系统最重要部分。 Eric Evans 曾提出如下问题助识别核心域:

    • 为何这系统值得写?
    • 为何不直接买个?
    • 为何不让外包写?

    若你对这几个问题的回答能帮你找到这个系统非写不可的理由,则它就是核心域。如电商系统的订单、商品服务。

    -- 支撑子域(Supporting Subdomain)

    不是你的核心竞争力,但又得做,市场无现成方案的子域。既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,但又必需。

    具有企业特性,但不具通用性,如:

    • 数据代码类的数据字典等系统
    • 排行榜,可能根据各种信息排名,这种东西没人会按你需要做个,但对你自己,又是扩展自己系统的重要举措
    • 调用银联、支付宝等第三方支付,即下游
    • 报表系统
    • 监控系统

    -- 通用域(Generic Subdomain)

    无太多个性化需求,同时会被多个子域使用的通用功能子域。如认证、权限等,无企业特点限制,无需太多定制化。行业里都这么做,即便不自己做,也不影响业务运行。

    如很多 App 要给用户发通知,这种功能完全可买个服务,丝毫不影响业务运行。

    五、DDD领域驱动经典四层设计

    脚本式编程(dao+service)与DDD领域驱动模式区别如下:

     

    5.1展现(表现层/用户接口层)(Presentation Layer):

    负责以Restful的格式接受Web请求,然后将请求路由给Application层执行,并返回视图模型(View Model),其载体通常是DTO(Data Transfer Object);

    5.2应用层(Application Layer):

    主要负责获取输入,组装上下文,做输入校验,调用领域层做业务处理,如果需要的话,发送消息通知。当然,层次是开放的,若有需要,应用层也可以直接访问基础实施层;应用层连接用户接口层和领域层,不应该实现领域模型的核心领域逻辑。主要职能:协调领域层多个聚合完成服务的组合和编排。应用服务主要负责服务组合、编排和转发,处理业务用例的执行顺序以及结果的拼装。在应用服务中还可以进行安全认证、权限校验、事务控制、领域事件发布或订阅等。

    5.3领域层(Domain Layer):

    领域层是领域模型的核心,主要实现领域模型的核心业务逻辑。领域模型的业务逻辑主要由实体和领域服务来实现。其中实体会采用充血模型来实现业务功能,如单一实体(或值对象)不能实现时,就交由领域服务进行组合和协调聚合内多个实体(或值对象),实现复杂的业务逻辑。

    主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Entities)的函数对外部提供业务逻辑的计算和处理;

    5.4基础实施层(Infrastructure Layer):

    主要包含Tunnel(数据通道)、防腐层,Config和Common。这里我们使用Tunnel这个概念来对所有的数据来源进行抽象,这些数据来源可以是数据库(MySQL,NoSql)、搜索引擎、文件系统、也可以是SOA服务等;Config负责应用的配置;Common是通用的工具类。

    基础层贯穿DDD所有层。其职能:提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。

    六、如何进行DDD领域驱动设计

    DDD 包括战略设计战术设计两部分,它们分别从不同的视角出发,完成领域建模和微服务的拆分和设计。事件风暴(Event Storming)是落实领域驱动的一种常用方法,使用事件风暴能够通过领域事件来识别出聚合根,组合的聚合根则又组成限界上下文,限界上下文则正是我们寻找的“微服务”的概念。

    6.1战略设计

    是从业务视角出发,划分业务的领域边界,建立基于通用语言和业务上下文语义边界的限界上下文,构建领域模型。而限界上下文就可以作为微服务拆分和设计的边界。以一种领域专家、设计人员、开发人员都能理解的“通用语言”作为相互交流的工具,在不断交流的过程中发现和挖出一些主要的领域概念,然后将这些概念设计成一个领域模型

    6.2战术设计

    则是从技术视角出发,侧重于对领域模型的技术实现,按照领域模型完成微服务的开发和落地。在战术设计中会有聚合、聚合根、实体、值对象、领域服务、领域事件、应用服务和仓储等领域对象,这些领域对象会以代码的形式映射到微服务中完成设计和系统落地。 由领域模型驱动软件设计,用代码来表现该领域模型。领域需求的最初细节,在功能层面通过领域专家的讨论得出。

    七、领域模型建模

    UML建模示例:

    1. @startuml
    2. hide circle
    3. note "知识库领域建模" as knowledge_uml #yellow
    4. entity 知识库 {
    5. kie_knowledge
    6. ==
    7. 知识来源:工单、设备
    8. 类型: 知识、案例
    9. 知识状态: 待提交、已发布、已撤销
    10. 查看次数:调用详情次数
    11. }
    12. entity 知识操作日志 {
    13. kie_knowledge_operation_log
    14. ==
    15. 操作名称
    16. 知识ID
    17. 操作人ID
    18. 操作人名字
    19. 是否成功(success)
    20. 操作类型: 存入草稿,撤销,发布
    21. }
    22. entity 知识关联对象 {
    23. kie_knowledge_linked_object
    24. ==
    25. 知识ID
    26. 对象上下文快照: jsonb
    27. }
    28. entity 知识点赞记录 {
    29. kie_knowledge_like_record
    30. ==
    31. 用户ID
    32. 知识ID
    33. 点赞标识:bool
    34. }
    35. 知识库 ||--|{ 知识操作日志
    36. 知识库 ||--o{ 知识关联对象
    37. 知识库 ||--o{ 知识点赞记录
    38. @enduml

     

  • 相关阅读:
    appium+python自动化测试
    欧拉计划第80题:平方根数字展开
    了解CSS Module作用域隔离原理
    jsp汽车销售管理软件Myeclipse开发mysql数据库web结构java编程计算机网页项目
    一文搞懂drag&drop浏览器拖放功能的实现
    【前端实例代码】使用HTML CSS 和 JavaScript制作一个五星评价的功能!可动态好评+差评+留言功能!
    log4j2---基于vulhub的log4j2漏洞复现---反弹shell
    cobbler3使用总结
    【LeetCode】图解算法数据结构+java代码实现(数据结构篇)
    【微信小程序】view和scroll-view组件的基本使用
  • 原文地址:https://blog.csdn.net/xinqing5130/article/details/130492600