from:http://www.jdon.com/soa.html
http://www.jdon.com/45728
http://www.jdon.com/cqrs.html
1.SOA:
首先Martin Fowler提出SOA歧义Service Oriented Ambiguity,认为"什么是SOA"是不可能回答,因为不同的人意味着不同的事情,SOA意味服务接口,意味流程整合,意味资源再利用,意味着管制,在下面SOA组件图中,服务和服务消费者(客户端)之间存在多个约束,当一个服务显式暴露后,客户端能够通过绑定定位到该服务,相当于两者签订了合同,规定了合同内容和如何实施,具体合同的描述是通过消息方式进行:
由于Java等传统语言主要是以类表达对象,将功能行为封装在类内部,而业务客户一般都是注重软件的功能,包括同行业公司系统之间数据交流也是以功能服务为接口,因此,面向服务的架构SOA更加贴近业务客户,也更适合业务伙伴之间流程整合,各个行业已经诞生自己行业特点的SOA,例如电信联盟的NGOSS,已经成为电信行业业务支撑系统BOSS的标准。SOA不但是技术用语,也是业务销售用语,通过服务这个中间概念,可以实现业务和技术之间的无缝转换,如今SOA已经和RESTDDD以及云计算等新技术方法结合。其内部主要概念有SCA ESB JBI等等,涉及工作流 规则引擎 消息总线等多个技术细节方面。
通常,一个架构师进行系统架构顶层设计时,必须考虑使用者的利益,不能单单实现软件的功能,还要考虑到软件的性能Scalable 可用性available/usable 安全性等软件质量,还要借鉴社区的最佳实践和经验形成的模式和反模式,避免重蹈覆辙和陷阱,再大胆采取最新的软件技术(比如用REST替代SOAP等)。
服务的提出其实隐含了两个概念,服务提供者和服务消费者,这两者之间有一个合同约定,这非常类似我们现实生活中签订的服务合同,A单位和B单位分别是服务的提供者和消费者,两者签订了一个服务合同,规定A为B提供某项服务。服务就是提供一些公共需求的设施,通过一个工作过程能提供帮助,使用,让使用者受益。
服务具体有如下:Windows Service:如PC定位者RPC Locator, 事件日志EventLog, DHCP Client,;. 软件服务Software Service,如分布式服务Distribution Service, 警告服务Alert Service 安全服务 Security Service, 日志服务;业务服务Business Service,如 帐号和客户服务,销售服务,订单服务,采购服务。
服务两个重要特点:自治和管制,自治代表服务不能被外部势力牵制,比如如果一个服务内部处理中需要调用外部资源或等待外部流程结束,这种等待不能影响服务本身的调用,如果一个服务分为显式对外和隐式内部两个部分,那么自治是针对隐式内部,意味着我们不能在具体一个服务中直接使用同步代码实现复杂功能。如:
public AServiceImpl implements AService{
public void productSale(...){
Product product = productService.getProduct();
int inventory = InventoryService.getInventory(product);
int price = priceService.getPrice(product);
}
}
在AServiceImpl的productSale方法中,我们获得商品的库存和定价,都是通过同步的RPC实现调用的,这样造成productSale方法依赖于InventoryService和priceService,无法实现自身自治。
实现服务真正自治,实际就是解决类之间依赖耦合的问题,消息是一种方式,但是基于消息又有两种通讯方式,基于请求响应和基于事件的EDA。
从服务自治可以看出,为什么要提出服务必须自治,因为服务是受管制的,在实际业务活动中,不同服务是被不同部分管理,比如定价服务归属财务部门系统,库存归属仓库系统,涉及系统之间调用协调不能自己使用同步RPC,而是需要消息。
在实际应用中,很多单位使用SOA主要看中其能够无缝整合新旧系统,称为EAI企业应用整合,下图是苏宁的一种SOA图,使用ESB企业服务总线这样的消息系统整合了新旧各种系统。
使用SOA和ESB能够灵活实现业务流程管理,工作流的管理BPM,如下图,一个订单的产生可能需要几个部门批准才能完成,而且这几个部门经常是变化的,如何灵活实现这种批准流程的定制也成为SOA实现的一部分,如下:
注意图中1 2 3 4 5 6 7 8 9标注的订单处理流程步骤,这种不同服务之间调用处理顺序可通过BPM进行灵活定制。
目前提供SOA全套解决方案和产品的厂商很多,包括IBM SAP和Oracle,国内金蝶用友浪潮软件等等,比如苏宁的SOA是以SAP为主的八国联军组装,既然SOA中间件服务商已经为我们提供了成熟的架构方案和产品,那么作为SOA使用者是否就无需顶层架构设计了呢?当然不是,SOA使用者要根据自己业务进行模块划分,进行领域建模设计,根据DDD领域驱动设计将业务分解为一个上下文模块,然后再用服务作为对外接口,内部封装的是DDD聚合根,而传统SOA作法是内部封装的是数据表的DTO,从而导致SOA服务内部腐烂堵塞,违背SOA自治和可用性等原则约束。具体可见DDD领域驱动设计。
1. 松耦合:由于服务自治,有一定封装边界,服务调用交互是通过发布接口。这意味着应用程序不感兴趣的服务如何被实现。
2.位置透明:服务的消费者不必关系服务位于什么地方。
3.可在异构平台间复用。可以将遗留系统包装成服务。
4.便于测试,能并行开发,较高可靠性和良好可伸缩性。
2.REST基础概念:
以状态为角度,提出将状态移植到客户端处理的新思路。 提出一个既适于客户端应用又适于服务端的应用的、统一的Web视图。适合B/S C/S S/S。 HTTP客户端与HTTP服务器之间的差别,对架构来说无所谓。一个软件应可以既充当Web客户端又充当Web服务器,而无须采用两套完全不同的APIs。
提供资源操作方法的统一:POST, GET, PUT, DELETE ,以超文本或超媒体驱动(hypertext/Hypermedia)的状态转移是REST架构核心。 操作带来状态变化,状态转移遍历使用链接导航方式实现。
如下图:首先通过GET方法访问/well-known-uri(1)获得当前所有资源(2),然后选择其中一个资源名FooService通过Get方法访问/well-known-uri/foo(3),这样得到foo下的资源列表。
foo可能是一个领域模型或其他代表业务核心的资源,假设foo是订单,用户如果希望改变订单状态,比如撤销订单,一旦点按撤销订单按钮,客户端将向/well-known-uri/foo/reverse发出PUT命令(5),代表撤销订单,这其实一个修改订单状态的命令。
客户端再次发出GET命令(6),获得状态已经改变的结果。
值得注意的是,当发出PUT命令后,不是通常由服务器端立即返回业务操作结果,而是返回Http的200,表示PUT操作完成,具体业务结果必须由客户端再次根据第三步获得的资源列表中URI资源,再次由客户端发出查询命令获得(6)。
-----------------------------------
3.DDD
Eric Evans的“Domain-Driven Design领域驱动设计”简称DDD,Evans DDD是一套综合软件系统分析和设计的面向对象建模方法,本站Jdon.com是国内公开最早讨论DDD网站之一,可订阅DDD专题。初学者学习DDD可从研究本站Jdon框架的DDD应用源码开始,戳这里开始。
过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。
DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。见下面DDD与传统CRUD或过程脚本或者面向数据表等在开发效率上比较:
服务器后端发展三个阶段:
DDD革命性在于:领域模型准确反映了业务语言,而传统J2EE或Spring+Hibernate等事务性编程模型只关心数据,这些数据对象除了简单setter/getter方法外,没有任何业务方法,被比喻成失血模型,那么领域模型这种带有业务方法的充血模型到底好在哪里?
以比赛Match为案例,比赛有“开始”和“结束”等业务行为,但是传统经典的方式是将“开始”和“结束”行为放在比赛的服务Service中,而不是放在比赛对象本身之中。我们不能因为用了计算机,用了数据库,用了框架,业务模型反而被技术框架给绑架,就像人虽然是由母亲生的,但是人的吃喝拉撒母亲不能替代,更不能以母爱名义肢解人的正常职责行为,如果是这样,这个人就是被母爱绑架了。
提倡充血模型,实际就是让过去被肢解被黑crack的业务模型回归正常,当然这也会被一些先入为主或被洗过脑的程序员看成反而不正常,这更是极大可悲之处。看到领域模型代码,就看到业务需求,没有翻译没有转换,保证软件真正实现“拷贝不走样”。
DDD最大的好处是:接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。
DDD是解决复杂中大型软件的一套行之有效方式,在国外已经成为主流。DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性,能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。领域模型的价值在于提供一种通用的语言,使得领域专家和软件技术人员联系在一起,沟通无歧义。
DDD在软件生产流程中定位i如下图,DDD落地实现离不开in-memory缓存、 CQRS、 DCI、EDA或Event Source几大大相关领域。
4.Actor
public class A {
private volatile int lower, upper; //两个状态值
public int getLower() { return lower; }
public int getUpper() { return upper; }
public synchronized void setAUpper(int value){
if (value < a.getUpper())
a.setLower(value);
}
public asynchronization void setALower(int value){
if (value > a.getLower())
a.setUpper(value);
}
}
上面这段代码业务逻辑是想实现lower命令查询的责任分离Command Query Responsibility Segregation (简称CQRS)模式是一种架构体系模式,能够使改变模型的状态的命令和模型状态的查询实现分离。这属于DDD应用领域的一个模式,主要解决DDD在数据库报表输出上处理方式。
Greg Young在infoQ的采访中“State Transitions in Domain-Driven Design”谈到了CQRS,Greg 解释了把领域模型分为两种:状态校验,以及状态转换,维持当前状态的一个视图。
在客户端就将数据的新增修改删除等动作和查询进行分离,前者称为Command,走Command bus进入Domain对模型进行操作,而查询则从另外一条路径直接对数据进行操作,比如报表输出等。
当一个Command进来时,从仓储Repository加载一个聚合aggregate对象群,然后执行其方法和行为。这样,会激发聚合对象群产生一个事件,这个事件可以分发给仓储Repository,或者分发给Event Bus事件总线,比如JavaEE的消息总线等等。事件总线将再次激活所有监听本事件的处理者。当然一些处理者会执行其他聚合对象群的操作,包括数据库的更新。
因为领域对象操作和数据库保存持久这两个动作分离,因此,数据表结构可以和领域对象松耦合(JiveJdon源码可展示领域对象和数据表不再是一对一对应依赖,这也是使用Hibernate 等ORM框架容易造成的问题),你可以优化数据表结构专门用于查询。
再者,由于事件驱动了领域模型的状态改变,如果你记录这些事件audit ,将可以将一些用户操作进行回放,从而找到重要状态改变的轨迹,而不是单纯只能依靠数据表字段显示当前状态,至于这些当前状态怎么来的,你无法得知。当你从数据库中获得聚合体时,可以将相关的事件也取出来,这些叫Event Sourcing,事件源虽然没有何时何地发生,但是可以清楚说明用户操作的意图。
虽然这种架构有些复杂,但是好处却很多,主要的是实现透明的分布式处理Transparent distributed processing,当使用事件作为状态改变的引擎时,你可以通过实现多任务并发处理,比如通过JVM并行计算或事件消息总线机制,事件能够很容易序列化,并在多个服务器之间传送,(EJB提倡贫血失血模型,实际就是为解决胖模型在多个服务器之间传送时序列化耗费性能,现在我们不序列化模型,而是改变模型数据的事件)。