SOA、REST 和六边形架构
SOA、REST 和六边形架构
上一篇:《IDDD 实现领域驱动设计-架构之经典分层》
阅读目录:
DDD 的一大好处就是并不需要使用特定的架构,经典分层架构只是一种,由于核心域位于限界上下文中,我们可以使用多种风格的架构,既然如此,我们应该把眼界看的更宽广些,有意思的东西多着呢。
SOA 和 REST 这两个货,我们都比较熟悉,他俩并不是由 DDD 引入,但却可以适用于 DDD。我个人觉得,要想把他俩发挥好,最好结合六边形架构(也可以称之为端口和适配器),至于原因,请接着往下看。

很重要的一张图,摘自:《实现领域驱动设计 P111》
1. SOA-面向服务架构
SOA(Service-Oriented Architecture),我没用过这货,下面说一下自己对于它的认识,可能不是很准确。
Service 意为服务,面向服务,也就是在 SOA 架构中,服务是核心。业务系统中分布着大量的业务模块,这些业务模块进一步提炼就是服务,然后通过规定的服务契约发布出来,这些服务集合起来就是 SOA 的核心,服务调用者可以是多样的,Web 端、移动端、桌面端都可以,也就是说它是分布式的架构,这些服务调用者调用的时候,要遵守发布者规定的一些契约和协议,而在服务本身或者之间也需要一定的契约和协议用来约束,要不然整个服务集合就会乱套,比如服务发布协议要有统一的规范,不能说一个服务是一个规范,服务之间也需要进行抽象抽离,尽可能的做到重用性等等。
因为我没用过 SOA,所以有些东西表达不出来,我用另一种概念去试着理解它,那就是 OWIN,他们可能不是一个领域的概念,但我觉得有些东西都是共通的,我们都知道 OWIN 体系结构中,有四大模块:Host、Server、Middleware 和 Application,他们之间最重要的一点就是组件化,并且任何一种模块都不依赖于彼此的任何一方,这就是自治,一种模块也构建不成 OWIN 的概念,这就是模块组合,一种模块的任意实现,都可以组合成整个 OWIN,原因是什么?因为他们彼此都遵守 OWIN 请求处理管道的协议。
回到 SOA 上面,你会发现,他们的概念其实是共通的,夏天到了,出去游玩,走着走着口渴了,我们就在路边买一个巨大的椰子,然后蜂拥而至一大堆基友,他们人手拿着一个吸管,而且种类和颜色各不相同,然后你的椰子上插满了各种习惯,看起来就像一个刺猬一样,你最后的下场可能就只有用刀划开个口子喝了,不管是用吸管,还是用到划开个口子,我们最后的目的都是喝椰子汁,只是使用的工具不同,一个椰子可能里面的汁更好喝,外面的更难喝。在这个日常生活示例中,一个椰子可以看作是一个 SOA 架构,内部就是各种小的服务(可以看作是一块一块的椰子肉,里面的更好吃),各种各样的吸管和刀子可以看作是服务调用者,而椰子皮也可以看作是服务协议,因为椰子汁需要椰子皮的包裹才不会洒出来。
SOA 架构原则:
- 服务封装
- 服务松耦合(Loosely coupled)- 服务之间的关系最小化,只是互相知道。
- 服务契约 - 服务按照服务描述文档所定义的服务契约行事。
- 服务抽象 - 除了服务契约中所描述的内容,服务将对外部隐藏逻辑。
- 服务的重用性 - 将逻辑分布在不同的服务中,以提高服务的重用性。
- 服务的可组合性 - 一组服务可以协调工作并组合起来形成一个组合服务。
- 服务自治 – 服务对所封装的逻辑具有控制权
- 服务无状态 – 服务将一个活动所需保存的资讯最小化。
- 服务的可被发现性 – 服务需要对外部提供描述资讯,这样可以通过现有的发现机制发现并访问这些服务。
SOA 相关资料:
2. REST 与 RESTful
RESTful 架构概念,是 Fielding 提出的,Fielding 这号人物就是 HTTP 协议的主要设计者之一。我们先看下 RESTful 这个词,ful 是跟在名词之后,表示程度,什么什么的,例如 helpful 乐于助人的,因此我们可以看出符合 REST 的架构就可以称为 RESTful,接着我们看下 REST,全称为“Representational State Transfer”,意为“表现层状态转化”。
在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。 -Fielding
这是 Fielding 在论文中所提到的,对于 REST 虽说是架构,但如果深入一点,就像是 HTTP 协议一样,可以看成一种规则或是协议。我们从一个地点到另一个地点,可以坐汽车、高铁、飞机等,对于 REST 就像是其中的一种交通方式,但 REST 的根本是 HTTP 协议,也就是说 REST 是基于 HTTP 协议的,这点就像坐汽车必须要有公路,坐高铁必须要有铁路是一样的道理,有时候为什么选用 REST,就像我们从南京到徐州,选择坐高铁而不选择坐飞机一样。
“Representational State Transfer”我们分解下:
- Representational 表现层:表现层表现什么,应该呈现资源(Resources),一个图片、一段文字、一个文件都成为资源,每个资源都用一个 URI(统一资源定位符)指向它,表现层就是调用 URI 把资源呈现出来,而且只是呈现,不做其他操作。举个例子:有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而 URI 应该只代表"资源"的位置。它的具体表现形式,应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对"表现层"的描述。
- State Transfer 状态转化:访问一个网站,就表示客户端和服务器发生一次交互行为,在这个过程中,就不发生数据和状态的转化,上面说到 HTTP 协议具有无状态性,如果客户端操作服务器,必须要状态转化,这个体现在表现层上,所以叫“表现层状态转化”。
通过上面的理解,可以总结下什么是 RESTful 架构:
- 每一个 URI 代表一种资源。
- 客户端和服务器之间,传递这种资源的某种表现层。
- 客户端通过四个 HTTP 动词(PUT、GET、POST 和 DELETE),对服务器端资源进行操作,实现"表现层状态转化"。
上面 REST 和 RESTful 的概念,摘自很久之前的一篇博文:初试ASP.NET Web API/MVC API(附Demo),并做了部分修改。
我再来说一下自己现在的理解,首先,REST 是一种架构风格,而不是一种架构,一种架构风格可以用多种架构进行实现,一个架构中也可能包含多种架构风格,这两者的关系,你可以理解为抽象和实现的区别,另外,REST 严格来说,应该属于 Web 架构的一种架构风格,因为它离不开 HTTP 协议。
REST 架构风格的两个关键:
2.1 资源(Resources)
Web 资源的表述是 URI,一个规范的 URI 就是开放出来的一个资源,它是唯一并具有一定的规范,对资源的操作方式就是 HTTP 提供的方法(PUT、GET、POST 和 DELETE),资源的表现形式是多样的,比如:JSON、XML、YAML 等。
我们看一下常用的 URI:
- GET: cnblogs.com/getUser/1
- POST: cnblogs.com/createUser
- PUT: cnblogs.com/updateUser/1
- DELETE: cnblogs.com/deleteUser/1
很显然,这种 URI 不符合 REST 对资源的定义,我们尝试修改一下:
- GET: cnblogs.com/user/1
- POST: cnblogs.com/user
- PUT: cnblogs.com/user/1
- DELETE: cnblogs.com/user/1
cnblogs.com/user/1,这个 URI 一般表述的含义是:Id 为 1 的 User 资源,这个仅仅是表述,URI 并不包含对这个资源的任何操作,所以,像 getUser、createUser 这类操作就不合适,资源的操作是通过 HTTP 提供的方法,还有一点是,比如 POST 中的cnblogs.com/user 和 cnblogs.com/user/1 有什么不同?第一种 POST,一般是创建一个新的 User 资源,创建完成后,一般会返回这样的一个 URI:cnblogs.com/user/1,第二种 POST,不是说创建一个 Id 为 1 的 User 资源,而是在 Id 为 1 的 User 资源下创建某种资源,你会发现,好的 URI 设计应该不包含动词。
2.2 状态(State)
首先,REST 是无状态的(Statelessness),我之前是一直不理解状态的含义,好像还把状态和资源格式(XML、JSON)混为一谈,现在想想确实太荒谬了,关于状态的几个要点:
- 状态分为:应用状态(Application State)和资源状态(Resource State)。
- 应用状态:与某一特定请求相关的状态信息。
- 资源状态:反映了某一存储在服务器端资源在某一时刻的特定状态。
- 客户端负责维护应用状态,而服务端维护资源状态。
- 服务器端不保有任何与特定 HTTP 请求相关的资源。
REST 中的无状态其实指的是应用状态,无状态的表现是服务端不保存应用状态,也就是客户端发起与请求相关的状态,应用状态是客户端在发起请求时提供的,那状态转化是什么意思?其实指的是服务端资源状态的转化,表现在客户端中,也就是上面所说的“表现状态转化”。
在 ASP.NET 应用程序中,我们都知道 Session 的概念,意为会话,也就是有关用户请求的会话,应该划分为应用状态,这个会话状态是保存在服务端的,从这一点上来说,这种设计就是 unRESTful 风格,REST 中的无状态,是客户端和服务端交互中所表达的一种概念,有时候,虽然应用状态可能不保存在服务端,但客户端发起的某些请求所表达的含义不恰当,也可以认为是不符合 RESTful 风格,比如客户端发起的请求中包含 Session ID,在服务端看来,客户端发起的这个请求,所表达的含义是要获取某个 Session,具体来说就是会话状态保存在服务端,这个虽然只是一个客户端请求的概念,但也可以认为这种设计是 unRESTful 风格。
总的来说,架构风格不是某一种具体架构,它是一种风格。
REST 参考资料:
3. 六边形架构
上面有关 SOA、REST 的讲述,丝毫没有 DDD 的半点影子,它们并不是为 DDD 而生,在架构设计的时候,你也可以单独使用它们,但对于整体 DDD 架构设计来说,总觉得会有些不对劲,这时候,就需要了解下六边形架构。
六边形架构(Hexagonal Architecture),又称为端口和适配器架构风格,其中的“六”具体数字没有特殊的含义,仅仅表示一个“量级”的意思,六边形的定义只是方便更加形象的理解。
我们知道分层架构的重要作用就是避免耦合的出现,经典分层架构和六边形架构都是分层架构的一种,但是所发挥的作用会有些不同,经典分层架构更多的精力放在抽象的分离上,每个层的职责分的很明确,各个层的依赖关系更加抽象化,从而避免耦合的出现,而在六边形架构中,是用“组件化”的形式来避免耦合的出现,每个业务单元尽可能的最小化,然后把这些业务组件集合起来,用一个锤子把他们都拍扁,所以,在整个集合中,这些小的业务单元都是“平等的”,这种方式用一个词来概括,那就是“扁平化”。
在博文一开始的时候,说过这样一句话:由于核心域位于限界上下文中,我们可以使用多种风格的架构。
那为什么核心域位于限界上下文中,DDD 就可以使用多种风格的架构?我们来分析一下,核心域指的是业务系统中的核心业务逻辑,这个通常用通用语言表述,限界上下文是一种边界,它包裹的是核心域,也就是说,核心域并不是组件化的形式表现,你可以把它看作是一种聚合概念,用限界上下文来进行限定,这个概念在之前的博文中有说明。对于业务系统来说,核心域毫无疑问是核心概念,DDD 的专注点也就是它,开发人员和领域专家会花大量的时间去探讨它,但对于架构设计来说,核心域只是业务上的专注点,并非是架构设计上的核心点,所以,也可以这样说,DDD 和架构设计,其实严格来说应该是两个领域方面的概念,他们的结合才真正构成整体的业务系统,换句话说,最烂的架构设计配上最好的 DDD(业务上的),这也是可以的,因为 DDD 专注的是业务实现,而并非是技术实现。
我们知道,经典分层架构分为四层,而对于六边形架构,一般会分成三层:
- 领域层(Domain Layer):最里面,纯粹的核心业务逻辑,一般不包含任何技术实现或引用。
- 端口层(Ports Layer):领域层之外,负责接收与用例相关的所有请求,这些请求负责在领域层中协调工作。端口层在端口内部作为领域层的边界,在端口外部则扮演了外部实体的角色。
- 适配器层(Adapters Layer):端口层之外,负责以某种格式接收输入、及产生输出。比如,对于 HTTP 用户请求,适配器会将转换为对领域层的调用,并将领域层传回的响应进行封送,通过 HTTP 传回调用客户端。在适配器层不存在领域逻辑,它的唯一职责就是在外部世界与领域层之间进行技术性的转换。适配器能够与端口的某个协议相关联并使用该端口,多个适配器可以使用同一个端口,在切换到某种新的用户界面时,可以让新界面与老界面同时使用相同的端口。
在六边形架构中,领域层和技术没半毛钱关系,可以看作是业务的技术实现,端口层包裹在领域层在外,外部要向和领域层“交流”,则必须通过端口层的“首肯”,反过来,领域层向外面“交流”也是一样,但这种方式一般是技术上的,比如领域对象的管理:
领域层想要获取某一个领域对象,来进行业务操作实现,然后告诉端口层说:“端口小弟,哥需要一个 XXX Domain Model,立马去叫人搞!”,端口小弟心想,老大发话了,得赶紧的啊,然后就在自己胸前,贴出了这样一段告示:能逮到 XXX Domain Model 的适配器杀手们,请速速到俺这里,必有重赏!告示一贴出,适配器杀手们蜂拥而至,然后根据自己的能力来进行判断,毕竟 XXX Domain Model 也不是那么容易擒服,也不是随便一个适配器杀手就能搞定的,这需要一定的能力,最后能做的适配器杀手进行揭此告示。
以上是即兴想到的一个情节,其实就是六边形架构,从内到外的一个过程体现,反过来,从适配器层到领域层也一样,这种方式一般是接受用户请求处理开始。其实,从某种意义上来说,端口层有点像应用层,只不过是拍扁之后的应用层,而适配器层有点像基础设施层,只不过是全能型的基础设施层,六边形是环形结构,所以表述起来更加形象。
还有一点是,端口层的存在,还有利于测试的进行,这些测试不是在领域层进行的,所以它丝毫不会影响领域层的进度,对于端口层的测试,一般是测试领域层中业务的正确性。
以上是六边形架构的一些概念,那再结合 SOA 和 REST,该如何实现呢?因为在六边形架构中,适配器层是以组件性质的方式提供服务,他和领域层进行联系要通过端口层,这个端口层可以看作是服务的一种协议或规范,这就是 SOA 和 REST 的用武之地,来扮演适配器层和端口层的角色。还有一个概念不同点是,服务交互分为服务端和客户端,对于 SOA 和 REST 本身来说,他们的实现就是服务端,但在六边形架构中,他们更像是一个客户端,并且表现形式是组件化的服务,而领域层是服务端,通过端口协议来调用组件服务提供一些操作实现。
SOA、REST 和六边形架构的结合,可以和一开始的那张图进行对比:

说明:本图摘自:《实现领域驱动设计 P115》
参考资料:
距离上一篇有很长的一段时间了,经典分层架构我是用过的,所以会有很多的感触,也有很多的文字需要表达,但对于 SOA、REST 和六边形架构,我并没有真正实质性的用过,所以,对于一个你不曾接触的概念,要把一些东西写出来是很难的,写不出来,那就看书、看文章、找资料,把有些有感触的文字记录下来,然后通过自己的理解再表达出来,这种方式过程虽然很慢,但还是有一定的效果。
关于 DDD 架构设计,还有很多很多的内容,比如 CQRS、事件驱动架构、网格分布式计算等等,这个需要时间来消化。
因为没有实践过,所以难免会有一些问题,还请大家斧正!!!
SOA、REST 和六边形架构的更多相关文章
- IDDD 实现领域驱动设计-SOA、REST 和六边形架构
上一篇:<IDDD 实现领域驱动设计-架构之经典分层> 阅读目录: SOA-面向服务架构 REST 与 RESTful 资源(Resources) 状态(State) 六边形架构 DDD ...
- SOA和微服务架构
微服务架构强调的第一个重点就是业务系统需要彻底的组件化和服务化,原有的单个业务系统会拆分为多个可以独立开发,设计,运行和运维的小应用.这些小应用之间通过服务完成交互和集成.每个小应用从前端web ui ...
- SOA和微服务架构的区别
微服务架构强调的第一个重点就是业务系统需要彻底的组件化和服务化,原有的单个业务系统会拆分为多个可以独立开发,设计,运行和运维的小应用.这些小应用之间通过服务完成交互和集成.每个小应用从前端web ui ...
- 使用WCF实现SOA面向服务编程—— 架构设计
原文地址:http://www.cnblogs.com/leslies2/archive/2011/03/29/1997889.html SOA本身就是一种面向企业级服务的系统架构,简单来说,SOA就 ...
- SOA的企业系统架构
基于SOA的企业系统架构设计及IT治理日记 (引) TOGAF是一个架构框架,指导做企业架构的标准和方法,简而言之,是一种协助开发.验收.运行.使用和维护架构的工具,核心是架构开发方法(ADM)指导了 ...
- SOA和微服务架构的区别?
转自知乎:https://www.zhihu.com/question/37808426/answer/93335393 SOA和微服务架构的区别? 微服务架构强调的第一个重点就是业务系统需要彻底的组 ...
- MVC、RPC、SOA、微服务架构之间的区别
MVC.RPC.SOA.微服务架构之间的区别 一.MVC架构 其实MVC架构就是一个单体架构. 代表技术:Struts2.springMVC.Spring.Mybatis 等等. 二.RPC架构 RP ...
- SOA面向服务体系架构
SOA概念 1.什么是SOA 面向服务的体系结构(Service-Oriented Architecture,SOA)是一个组件模型. 它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的 ...
- 在 .NET Core 中应用六边形架构
在本文中,您会看到一个Web API应用的模板,在.NET Core 中应用了六边形架构,并且里面包含了一些基础功能. 介绍 这是一个模板项目,里面集成了一些必备的基础功能,当我们需要开发一个新项目时 ...
随机推荐
- TCP连接建立过程中为什么需要“三次握手”(转)
传输控制协议(Transmission Control Protocol, TCP)是一种面向连接的.可靠的.基于字节流的运输层(Transport layer)通信协议.是专门为了在不可靠的互联网络 ...
- App如何选择移动广告平台,开发者2 - 移动变现模式分析
开发人员社区的上述分析.它可能无法覆盖全部,但是,每一个开发者都需要根据自己的特点变现模式选择.继App流动性模式做了全面的分析. 游戏.广告.电商是互联网的三种变现模式,移植到移动互联网也相同适用. ...
- leetcode第一刷_Spiral Matrix II
跟上一题的策略全然一样,这个题是要求保存当前增加的是第几个数,由于矩阵里面存的就是这个东西. 我有尝试想过是不是有一种方法能够直接推算出每一行的数据是哪些.但没过多久就放弃了.这样的方法尽管能够避免在 ...
- 腾讯2014在广州站实习生offer经验(TEG-开发背景)
研究在过去的一年是linux 什么系统编程和网络编程.比较熟悉的语言c/c++,python只写一些测试client.是后台开发类,比方前面笔面的网易CC(面完hr后挂).大概3月15号就在腾讯 jo ...
- hdu 4465 概率称号
http://acm.hdu.edu.cn/showproblem.php?pid=4465 第一直觉概率DP但很快被否定,发现只有一个简单的二项分布,但感情的表达,没有对生命和死亡的例子.然后找到准 ...
- Windows Phone 启动器
http://msdn.microsoft.com/zh-CN/library/gg278408(v=vs.92)#BKMK_Launchers using Microsoft.Phone.Contr ...
- 思考的工作方式——计划经济or市场经济
背景:单位成立了技术领先的基础部门.专注于产品规划的技术解决方案部门.产品的发展规划方向.批准的项目和各部门的其他工作方案.工作内容是在这一领域没有问题.毕竟,从过去企业发展的一个部门模型现在是一个功 ...
- PLSQL:[1]plsql中文乱码,显示问号
PLSQL运行sql语句,不识别中文.输出的中文标题显示成问号?? ?? 工具/原料 PLSQL Developer 9 方法/步骤 1 登陆plsql,运行sql语句.输出的中文标题显示成问号??? ...
- Linux内核分析(五)----字符设备驱动实现
原文:Linux内核分析(五)----字符设备驱动实现 Linux内核分析(五) 昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷, ...
- MVC 定义JsonpResult实现跨域请求
MVC 定义JsonpResult实现跨域请求 1:原理 在js中,XMLHttpRequest是不能请求不同域的数据,但是script标签却可以,所以可以用script标签实现跨域请求.具体是定义一 ...