写在前面
领域驱动设计DDD (Domain Driven Design)提出了从业务设计到代码实现一致性的要求,不再对分析模型和实现模型进行区分。也就是说,从代码的结构中我们可以直接理解业务的设计,命名得当的话,非程序人员也可以“读”代码。这与微服务设计中的约定优于配置不谋而合,如果你熟悉英文,那么根据包名和类名就可以解读出程序开发者所构建的业务的大概意图。
领域模型包含一些明确定义的类型:
实体是一个对象,它有固定的身份,具有明确定义的“连续性线索”或生命周期。通常列举的示例是一个Person (一个实体)。大多数系统都需要唯一地跟踪一个Person,无论姓名、地址或其他属性是否更改。
值对象没有明确定义的身份,而仅由它们的属性定义。它们通常不可变,所以两个相等的值对象始终保持相等。地址可以是与Person 关联的值对象。
集合是一个相关对象集群,这些对象被看作一个整体。它拥有一个特定实体作为它的根,并定义了明确的封装边界。它不只是一个列表。
服务用于表示不是实体或值对象的自然部分的操作或活动。
领域模型在实现时可大可小,在业务的早期,在系统比较小的情况下,它有可能是一个类。当系统做大了以后,它可能是一个库。再做更大- -点的时候,它可能是一个服务,给不同的应用去调用。
要将领域元素转换为服务,可按照以下一般准则来完成此操作:
使用值对象表示作为参数和返回值,将集合和实体转换为独立的微服务。
将领域服务(未附加到集合或实体的服务)与独立的微服务相匹配。
每个微服务应处理一个完整的业务功能。
从今天开始,咱们就来一块学习微服务的设计原则!!!
分层架构
同一公司使用统一应用分层,以减少开发维护学习成本。应用分层看起来很简单,但每个程序员都有自己的一套方法,哪怕是初学者,所以想实施起来并非那么容易。
最早接触的分层架构应该是我们最熟悉的MVC (Model-View-Controller) 架构,其将应用分成了模型、视图和控制层,可以说引导了绝大多数开发者。而现在的应用(包括框架)中非常多架构设计都使用此模式。之后又演化出了MVP ( Model-View-Presenter)和MVVM(Model-View-ViewModel)。这些可以说都是随着技术的不断发展,为了应对不同场景所演化出来的模型。而微服务的每个架构都可以再细分成领域模型,下面看一下经典的领域模型架构。
它包括了Domain、Service 和Repositories。核心实体( Entity)和值对象(Value Object)应该在Domain层,定义的领域服务(Domain Service) 在Service层,而针对实体和值对象的存储和查询逻辑都应该在Repositories 层。值得注意的是,不要把Entity 的属性和行为分离到Domain和Service两层中去实现,即所谓的贫血模型,事实证明这样的实现方式会造成很大的维护问题。基于这种设计,工程的结构可以构造为:
- MicroService Sample/src/
domain
gateways
interface
repositories
services
当然 ,在微服务的架构中, 每个微服务不必严格遵照这样的规定,切忌死搬硬套,最重要的是理解业务。在不同的业务场合,架构的设计可以适当地调整,毕竟适合的架构定要具有灵活性。
分层的原则如下。
文件夹分层法
应用分层采用文件夹方式的优点是可大可小、简单易用、统一规范,可以包括5个项目, 也可以包括 50 个项目,以满足所有业务应用的多种不同场景
调用规约
在开发过程中,需要遵循分层架构的约束,禁止跨层次的调用。
下层为上层服务
以用户为中心,以目标为导向。上层(业务逻辑层)需要什么,下层(数据访问层)就提供什么,而不是下层(数据访问层)有什么,就向上层(业务逻辑层)提供什么。
实体层规约
Entity是数据表对象,不是数据访问层对象; DTO是网络传输对象,不是表现层对象; BO是内存计算逻辑对象,不是业务逻辑层对象,不是只能给业务逻辑层使用。如果仅限定在本层访问,则导致单个应用内大量没有价值的对象转换。以用户为中心来设计实体类,可以减少无价值重复对象和无用转换。
U型访问
下行时表现层是Input, 业务逻辑层是Process, 数据访问层是Output。上行时 数据访问层是Input,业务逻辑层是Process,表现层是Output。
通信协议
接口调用如果是远程调用,那么就构成了简单的分布式。最简单的远程接口实现方式是Web Service或REST.当然一一个 合理的分布式应用不仅仅是远程接口调用这么简单,还需要有负载均衡、缓存等功能。实现分布式最简单的技术是REST接口,因为REST接口可以使用现存的各种服务器,比如使用负载均衡服务器和缓存服务器来实现负载均衡和缓存功能。
关于通信协议,不同的公司有不同的选择,但是建议同一公司内部使用统--的通信协议,比较典型的有GRPC和BRPC。
GRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。目前提供C、Java和Go语言版本,分别是GRPC、GRPC-Java, GRPC-Go,其中C版本支持C、C++、Node.js、Python、Ruby、 Objective-C、 PHP和C#。
GRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单TCP连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
与GRPC类似,BRPC 源自百度,目前支撑百度内部大约75万个同时在线的实例。
其实基于以上的几种选择都能够完成高效的开发,团队内部使用统一-的标准,这样更有利于模块化和统一标准。
服务间的通信通过轻量级的Web服务,使用同步的REST API进行通信。在实际的项目应用中,一般推荐在查询时使用同步机制,在增删改时使用异步的方式,结合消息队列来实现数据的操作,以保证最终的数据一致性。
REST API应为创建、检索、更新和删除操作使用标准HTTP动词,而且应特别注意操作是否幂等。
POST操作可用于创建资源。POST 操作的明显特征是它不是幂等的。举例而言,如果使用POST请求创建资源,而且启动该请求多次,那么每次调用后都会创建一个新的唯一资源。
GET操作必须是幂等的且不会产生意外结果。具体来讲,带有查询参数的GET请求不应用于更改或更新信息(而应使用POST、PUT或PATCH)。
PUT操作可用于更新资源。PUT操作通常包含要更新的资源的完整副本,使该操作具有幂等性。
PATCH操作允许对资源执行部分更新。它们不一定 是幂等的,具体取决于如何指定增量并应用到资源上。例如,如果一个PATCH操作表明一个值应从A改为B,那么它就是幂等的。如果它已启动多次而且值已是B,则没有任何效果。对PATCH操作的支持仍不一致。例如,JavaEE7中的JAX-RS中没有@PATCH注释。
DELETE操作用于删除资源。删除操作是幂等的,因为资源只能删除一次。但是,返回代码不同,因为第一次操作将成功(200), 而后续调用不会找到资源(204)。
单一职责
设计原则很重要的一点就是简单, 单一职责也就是我们经常所说的专人干专事。
一个单元(一个类、函数或者微服务)应该有且只有一个职责。无论如何,一个微服务不应该包含多于一个的职责。职责单一的后果之一.就是职责单位(微服务、类、接口、函数)的数量剧增。据说Amazon、Netflix这些采用微服务架构的网站一个小功能就会调用几十上百个微服务。但是相较于每个函数都是多个业务逻辑或职责功能的混合体的情况,维护成本还是低很多的。
SRP (单一职责原则)中的“单一职责”是个比较模糊的概念。对于函数,它可能指单一的功能,不涉及复杂逻辑;但对于类或者接口,它可能是指对单一对象的操作,也可能是指对该对象单一属性的操作。总而言之,单一职责原则就是为了让代码逻辑更加清晰,可维护性更好,定位问题更快的一种设计原则。
单一职责的优点如下:
类的复杂性降低,实现什么职责都有清晰明确的定义。
可读性提高,复杂性降低。
可维护性提高,可读性提高。
变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
记得在三字经里边有这样一段:
教之道,贵以专
说的就是无论学习还是构建团队,最重要的是专才,而不是全才。就好比一个足球队,如果都是前锋或者都是后卫,那么这样的球队一定不会出好成绩,反而是将各个位置上的人进行统一协调,根据分工不同,共同协作,形成1+1>2的效果,那么这样的团队就非常容易出好成绩。
有很多公司为了赶进度,经常会招聘- -些所谓的全能型人才,但是这种人往往专业程度不够高,当遇到某些棘手的问题时,往往不能够非常快速地解决问题。从而导致最终交付的软件质量较差。
实施单一职责的目的如下:
以类来隔离需求功能点,这样当一个点的需求发生变化时,不会影响别的类的逻辑,这个和设计模式中的开闭原则类似,对于扩展持开放态度,对于修改持关闭态度。
是一个原子模块级的粒度,至于原子的粒度到底是什么样的,应该因业务而异,设计的过程中同时考虑业务的扩展,所以这就要求在设计的过程中,必须有业务专家共同参与,共同规避风险。
粒度小,灵活,复用性强,方便更高级别的抽象。
每个微服务单独运行在独立的进程中,能够实现松耦合,并且独立部署。
微服务近几年大火,里面的核心知识更是数不胜数,小编这里只是整理了一部分设计原则,后续小编会持续更新,保证朋友们每天接收新内容,每天进步一点点~~~
喜欢文章请多多点赞评论转发,关注小编,你们的支持就是小编最大的动力!!!