星期四, 03月 5th, 2009
SOA模式短文推荐
强烈推荐SOA爱好者下载收藏《SOA Pattern》一文,又有精品全文《SOA Design Pattern》推荐下载

作者将模式分为三类:(1)基本服务模式,是最小粒度的服务模式,已经无法再次拆分,可以认为是模式原语,多用在服务层面使用。(2)架构模式,通用的SOA系统设计要素,多用在系统设计时,做为架构风格的一个元素。(3)复合模式,基本服务模式的组合,用来定义系统的衔接特征,多用在应用系统整合场景。下面按照自己的理解和使用经验,简单说明一下,具体的介绍还请研究原文。
基本模式说明:
- Aggregator:将多个独立的消息体通过该计算组件组合为一个消息体。
- Service Bus:将多个独立的系统(已有系统、新系统)通过统一机制整合起来。
- Dynamic Routing:与规则库配合,实现一个消息体的路由,实现上常采用基于消息头路由、基于内容路由2中情况。
- Event-Driven Consumer:常用在资源有限的场景,解决资源竞争的问题,例如,网络接入时,系统所能接收到socket有受限的,此时即可通过该模式解决。
- Filter:消息内容的过滤,例如,关键字过滤,也可用在信息补全场景。
- Router:将消息发送到不同的应用,与Dynamic Routing的区别在于,此时规则是固定的,常常有2中使用方式,单播、多播。
- Translator or Transformer:用在消息格式的转换场景。
架构模式说明:
- Asynchronous Processing:异步事件风格解决服务之间的交互,例如,ATM到银行账务系统,多用该模式。在实践中,也鼓励使用这种风格,实现SOA,以弱化一致性、事务性,缓解资源受限等场景,该模式需要业务配合才能完全发挥。
- Bridge:常用在2中体系之间的交互,把一种协议转换为另一种协议。它与Translator的区别在,Translator是针对消息的,它针对链路。
- Cross-Service Operation:把多个服务封装为一个服务来使用,以确保一致性、事务性,可以把该风格弱化为分布式事务风格。
- Event-Driven Dispatching:基于事件的派发,常用在pub/sub场景。
- Process Aggregation:把多个过程聚合在一起,按照一定顺序进行消息处理,此时不要求一致性,每个过程都是独立的,用BPM技术更容易理解该场景。
- Routing and Filtering:实现一个消息的多种渠道应用,例如,一个业务通知,通过Mail,IM、SMS、语言、甚至特殊应用传达给用户的场景,一个产品为多个渠道提到内容的场景。
- Replicator:实现一份消息,服务处理的时候,同时往数据库复制一份,例如,数据备份容灾、数据采集、数据审计等。
复合模式说明:
- Centralized Schema:解决跨应用边界的数据共享问题。
- Concurrent Contracts:解决一个服务多个消费者时,每个消费者接口不同的问题。
- Decomponse Capability:??
- Enterprise Service Bus:通过一个消息总线,解决应用整合的问题,化解复杂的网状链接。
- Fault-Tolerant Service Provider:通过负责均衡解决服务高可靠性的问题。
- Wrapper:解决传统应用服务化的问题。
星期五, 02月 27th, 2009
ESB产品Mule+Spring实现最简领域驱动框架
更多内容在:
架构专栏
先发一个消息,开源ESB产品Mule又发布了新的LDAP组件,很是让人佩服,首先赞一个先。
架构风格的基本组成体:计算组件、连接组件、信息、数据、配置。下图中,又抽象了一个领域组件,这也是为了让这个风格表示法能够同样适用于业务层面。

MULE+Spring完全支撑上图所示的架构风格,我们可以很容易的用它们实现最简领域驱动框架,以支持领域驱动设计。MULE大量提供连接组件(通信,协调,仲裁等)和信息链路拦截机制(可使用格式转换,加密,压缩之类的),也提供一些通用的计算组件,但更多的通用计算组件还是依赖自己开发;Spring提供了配置能力,把组件之间的关系记录下来;唯独留下新引入的领域组件(领域组件的计算能力可以很容易通过连接组件实现与其它组件的交互),需要我们自己来开发。
在《领域驱动设计在SOA架构中的实践》一文中提到技术架构师解决如下问题:“解决缓存、事务管理(包括分布式事务)、分布式接口规范、通信机制、共性系统服务的注入和应用契约、领域驱动设计风格与编程模式的确定、测试方案的确定、工具的提供、搭建可运行的环境、跨环境(分布式环境)的服务依赖策略、模型转换策略和工具的提供、集成策略”。下面用Mule+Spring的组合来对应一下:
- 缓存:是一个计算组件,需要单独开发。
- 事务管理:是一个计算组件,利用Spring事务,分布式事务需要单独定制一个计算组件。
- 分布式接口规范:需要单独定义。
- 通信机制:MULE提供。
- 共性系统服务的注入和应用契约:Spring提供。
- 领域驱动设计风格与编程模式的确定:见实践一文。
- 测试方案的确定:Spring提供单元测试,Mule提供分布式测试,可以定制一些测试辅助组件,例如:数据生成组件,该组件配合MULE的定时组件,很容易实现简单的自动测试。
- 工具的提供:MULE,Spring都提供了工具,DAO生成组件依据存储框架的不同寻找合适的。
- 搭建可运行的环境:再搞一个JBOSS,整个环境OK。
- 跨环境(分布式环境)的服务依赖策略:见实践一文,Spring + Mule提供技术支持。
- 模型转换策略和工具的提供:用Dozer 解决Bean-Bean,JAXB解决Bean-XML,或者Hessian解决Bean-Hessian;开发这些计算工具,配置在Mule的链路中,即可使用。
- 集成策略:Mule提供了各种连接组件,轻易集成外部系统或服务。
- 额外的:Mule提供了分布式计算环境,可以作为SOA技术平台来使用。
- 额外的:Mule提供了服务模型,可以作为服务平台来用。
- 额外的:Mule提供了Galaxy,解决服务治理。
通过上面的匹配,就会发现,以MULE+Spring为基础,实现一个更强大的领域驱动框架将会非常容易。
星期五, 02月 27th, 2009
领域驱动设计在SOA架构中的实践
更多内容在:
架构专栏
本着实践–阅读–思考–再实践的原则,总结一下领域驱动设计在SOA架构中的实践,关于领域模型设计自身,我建议大家阅读《领域驱动设计》一书,关于领域驱动实践的入门教程,我强烈建议阅读《领域驱动设计和开发实战》;关于领域驱动实践的再一个实践入门,我建议阅读《Domain-Driven Design in an Evolving Architecture》。2篇文章中,作者还提供了架构图,它们已经能够满足大部分应用的架构需求。
1.为什么要在SOA架构中使用领域驱动设计?之所以采用SOA,不是因为好玩,而是业务复杂性导致的,迫不得已而为之呀(可参考《SOA、服务化》栏目内的一些文章)。使用了SOA架构,不代表业务复杂性自动消失,还是从业务入手分析之,从目前广泛流行的方法学中,领域驱动设计完全符合符合这个需求,它就是为解决业务复杂性而生的。
2.职责分离。有时候,由于资源的情况,会让一个人同时承担技术架构和业务系统分析的角色,这种情况下,往往会搞的这个领域驱动设计乖乖的,简单玩玩也是无所谓了,对于较大的互联网公司,就是比较头疼的事情了,应用系统的开发者不同,这架构风格绝对会千奇百怪。因而还是要把这职责分清楚,搞一个技术架构师,再搞一个业务系统分析师。
- 技术架构师:关注与领域驱动设计在SOA架构中的技术部分,重点在“解决缓存、事务管理(包括分布式事务)、分布式接口规范、通信机制、共性系统服务的注入和应用契约、领域驱动设计风格与编程模式的确定、测试方案的确定、工具的提供、搭建可运行的环境、跨环境(分布式环境)的服务依赖策略、模型转换策略和工具的提供、集成策略”。这些东西确定下来之后,技术架构风格就完全定型,下面的工作就是把架构风格“固化”,此时有2中策略,固化为框架、固化为产品,建议在头2个项目迭代中,以框架形式提供,之后就可以考虑以产品方式提供(可参考通用技术系列文章)。有了“领域驱动”在SOA架构中的支撑框架(产品),将大大的简化业务系统分析师的工作,但距离成功还有70%的距离(只有业务成功了,才是成功),可这30%将占据80%的质量属性,严重影响全局SOA架构体系的成功。
- 业务系统分析师:关于业务,关注领域驱动设计,请千万忘记技术,相信我们的战友会解决这些问题的。
3. 领域驱动设计概念架构风格图A,该风格比较保守一下,在具体cache的使用上,有2中选择,或者领域中使用,或者仓储中使用,具体那种方式适合,要看领域模型和缓存需求,不是啥都适合通过仓储缓存。

4. 领域驱动设计概念架构风格图B,该模型的变化之处在增加了一个事件层。好处:领域模型与仓储模型、cache模型等隔离,同时提供了扩展机制;缺点:不如风格图A那么直接,容易理解,且事件层放在这儿有点不伦不类。在使用中注意几点:事件对象可以业务域统一一个,或者技术框架层统一一个;建立事件标识契约,例如领域对象的类型;保证事务,避免跨线程。

5.注意以下几点:
- 领域切分不要太小,切不可变成简单的分布式方法的调用。
- SOA重用的是服务,不是功能。
- 服务应该与需求用例对应;服务提供的方法与设计用例对应(设计用例是对需求用例的实现)。
- 领域内部的复杂度与业务能力体系相关,领域不是平面的、是树状的、是有层次的,可以用层模式来切分领域对象。
- 领域内部既有用户看见的东西,也有用户看不见的业务内能力,对外暴露的值对象信息,往往来自最上层的领域对象。
- 即使一人身兼2职,也要把自己切分开,搞技术架构时,忘记具体业务,把架构风格固化下来,请开发程序员按照这个编程模型来开发;搞领域驱动设计时,完全忘掉技术。
- 如果作为公司的架构师角色进入项目团队,那么请:走入程序员中间,走入项目中间,但不要走入业务编码中间;到这儿是获取需求,验证框架(验证架构风格),发现问题的;目标是整个SOA架构的成功,不仅仅是项目的成功。
- 服务对象层,请参考《SOA实践之:Java服务接口设计的一些实践准则》,务必形成严格的编程规范,强制遵守。
星期日, 06月 29th, 2008
SOA是可持续的战略,不是一次项目
常常听到如下关于SOA的说法:
- 我们完成了SOA化改造。
- 我们的SOA化,已近实现了80%。
- 这次项目是为了实现SOA化,因此要请有这方面经验的公司来实施。
- 从技术角度层面来说,我们达到了SOA化的目标。
- 这是我们最后一个SOA个项目
- 等等
如果这些话出自程序员,项目经理等人员,也是无所谓的;如果出自IT管理层的人员,哪就是一种可怕的想法了。因为他把SOA仅仅看作是一次项目了,而没有把它作为战略。这样的后果也是可以预见的,最终本次投资将会失败。
因此,建议那些准备实施SOA的公司,首先回答这个问题:SOA是贵公司的一个战略吗?
为什么要回答这个问题?
- 从SOA概念来看
关于什么是SOA,我有几篇文章,可以参考(《这些都不是SOA》,《ESB产品升级准备:SOA、ESB、JBI、SCA、OSGI概念再学习、再理解》,《SOA,想说爱你不容易》)。丛概念上可以看出,SOA不少技术问题,它是一种思想,一种架构,因而不可能通过一个项目就把这种思想或架构完全实现;思想也是会发展得,今天的思想在明天或许就会不适应。
- 从SOA目标来看
SOA的目标是解决业务敏捷性问题。如今,大环境复杂多变,任何一个公司都不敢说自己的业务就是这几个、自己的业务会一直不会变,很多企业,在很短的周期内,就会推出各类产品(特别是服务类企业,例如:银行、互联网企业等)。只有那些能够快速适应变化,主动寻求变化的企业才能够生存下来,发展起来,例如:会跳舞的大象IBM,IT行业最大的特色就是变化快、发展快,如果IBM的业务不能敏捷的适应这种变化,它还能跳舞吗?正因为IBM深刻理解了SOA的目标,因而它把SOA看作一种战略–可持续的战略。
- 从IT管理角度来看
对于IT主管,一定要确保本公司的IT战略是可持续的,不能今天一种想法,明天又看到什么好思想,就换了一种想法,或者换个主管,就彻底换了战略(这种做法,就像我们国内的城市建设,路总是修不好)。既然从一种思路切合到了SOA领域,那么再次切换的成本将会更高,也不可能多种思路同时进行(吃得多,嚼不烂)。此外,谁能保证一次项目就确保几年内业务变化的需要?从我们实施的经验来看,必须持续的进行SOA化,才能确保SOA持续的发挥效果。
- 从系统分析角度来看
每次项目都要进行系统分析,都是基于当前的业务进行,那么不可避免的就会有一些盲点(从认知的角度看,人不可能一次就抓住事物的本质,何况大多数系统分析员不具有这种能力)和不合理支出。这些问题,只有在实施后一段时间才能发现(我们自己的实践经验);此外随着补丁的实施,原有的系统已经恶化,和最初的SOA化目标越来越远。此时,如果不能再次用SOA的思想重新审视系统,就将彻底失去了SOA带来的好处。
- 从技术特征来看
SOA非但不能让系统架构变得简单,反而变得复杂了(系统设计要求变高了、系统部署复杂了、发布复杂了、各类SOA相关技术的引入增加了对人员的要求),这也是为什么要引入SOA治理。从这一点来看,只有持续的SOA化,才能最终发挥SOA技术架构的好处。
再次提醒将要进行SOA化的朋友和已经进行了SOA化的朋友,把SOA当作贵公司的可持续的战略来看,而不要认为它紧紧是一次项目。
星期五, 06月 27th, 2008
SOA实践之:Java服务接口设计的一些实践准则
在SOA领域,一些OO领域的接口设计原则具有很好的借鉴意义,但随着SOA化带来的分布式特性,有些东西就需要进行特殊处理。下面就我们进行SOA化过程中接口设计过程中,曾经比较容易出现过问题的地方进行部分总结。
【术语说明】
服务与业务组件的定义:
- 业务组件:最小的业务逻辑单元,例如:密码验证组件、登录组件。
- 服务:业务模块提供给外部的核心业务逻辑,服务组合业务组件。例如:修改密码服务,会使用密码组件的验证服务,也会使用登录组件的修改密码服务。
【说明:】可否密码验证组件的功能合并到登录组件哪?可以的,此处之所以单独分离,与我们提供多种密码验证机制有关,登录组件对应的业务并未完全涵盖密码验证组件的功能。
- 服务接口的方法,禁止重名(overload特性)。大多数序列化工具【例如:XFire】对重名方法进行处理时,都不可能避免的存在或多或少的问题。为了减少麻烦,还是放弃OO的这一特性,或者在面向服务领域,重载本身就不是一个特性。
- 服务接口的方法,避免返回异常【我们是禁止异常,包括业务异常、系统异常】。这个要求看起来违反了一些对象设计的原则,从实际使用角度来看,具有如下好处:很多工具对异常序列化很容易出现错误;一不小心,外部使用者还会产生异常依赖;需要外部使用者自己把异常翻译为业务语义【从业务角度来说不是件好事情】。
- 服务接口必须有清晰的文档说明,对于常量性质的参数,要列出支持的常量值。
- 服务接口尽可能把查询和业务方法分离【非必须】。好处是使用AOP时简单方便,对于查询方法进行特殊处理时也比较方便,例如:与cache整合、与搜索引擎整合、与特殊权限要求进行整合(例如,对返回信息按照权限进行过滤)等。
- 服务接口参数排列按照如下原则:操作主体对象或ID,业务参数{…},环境参数。
例如:verifyPassword(String operatorId, String password, ServiceContext serviceContext)
operatorId是操作主体的ID。
Password是密码。
serviceContext是环境参数。
- 业务处理类接口,返回值可以采用统一对象模式。例如:ServiceResult:
public class ServiceResult<T> implements Serializable {
private static final long serialVersionUID = 760567785564620157L;
private boolean isSuccess;
private String resultCode;
private T resultObject;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
public String getResultCode() {
return resultCode;
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public T getResultObject() {
return resultObject;
}
public void setResultObject(T resultObject) {
this.resultObject = resultObject;
}
}
采用这种模式,实际是按照协议的角度进行设计,所有服务按照统一的包装模式返回。
-
服务接口的参数避免使用Object类型,集合类型要指定具体对象类型。原因:当使用Object时,使用者获取到WSDL文件时,不知道该传递什么业务数据,如果是其它语言的调用者,更无法理解了;这类接口设计违反了业务SOA的原则,暗含着如下潜台词:业务上不明确的,不清晰的。
- 服务接口避免使用集合类型,尽可能使用数组类型。
- 服务接口中使用枚举的原则:如果是对外界发布,避免使用枚举;如果使用者众多,避免使用枚举;如果是内部几个专用系统使用,可以考虑使用枚举,可以参考《枚举产生的系统耦合性》。【文档的清晰化,可以化解常量参数不明确的问题】
- 服务接口避免设计成面向数据的操作,例如:updateStatus。这类接口,通常包含太多的业务含义,甚至没有业务含义,需要外部使用者去理解业务内部的细节,否则无法进行操作。这也是我们设计服务接口时,需要特别注意的地方,不要让外部使用理解业务内部是如何存储的,结构是什么样的,这样会导致业务耦合度过高,服务内部发生重构,变化时,外部使用也需要进行变化。
- 服务接口层建议设计一个统一的模板框架,来处理性能分析、特殊参数处理(例如:ServiceContext)、事务处理、异常处理、日志处理、事件处理、审计处理等具有共性的行为。
- 服务接口的方法名避免以:is,get开头,用find,check等来代替之,避免Java序列化工具把该类当作Java Bean进行处理。
- 业务组件中异常的要求:所有的业务组件,如果发现违法业务原则的地方,需要中断程序的执行,那么throw统一业务异常,该业务异常可以借鉴如下方式设计:
public class ComponentException extends Exception {
private static final long serialVersionUID = -6900280471536579015L;
private ErrorCodeEnum code = null;
public ComponentException ErrorCodeEnum code) {
super();
this.code = code;
}
public ComponentException ErrorCodeEnum code, Throwable e) {
super(e);
this.code = code;
}
public ErrorCodeEnum getCode() {
return code;
}
public void setCode(ErrorCodeEnum code) {
this.code = code;
}
}
坏处:从异常名看不出明确的业务语义。
好处:避免服务层需要捕获多种异常;业务语义通过ErrorCodeEnum返回到业务层。
- 业务组件内部尽可能少的进行事务处理【除非有特殊的需求】、异常处理【除外业务组件自身组合了外部的业务服务,此时需要处理异常】。
好处:统一由服务层进行处理,可以避免事务处理的错误。不进行异常处理,可以避免异常转义时,出现信息丢失;此外,如果使用了统一异常,那么就更不能在业务组件内部进行异常处理了,否则会发生原始业务限制规则被丢失的可能。
对于一些基本原则,可以参考《SOA实践之:在程序层,面向服务的设计准则探讨》一文。
使用方,推荐使用代理模式来使用外部服务。
1、可以进行一些特殊处理,例如:网络异常处理,远程性能评估等
2、对内部提供重业务层面的方法,屏蔽环境参数
3、外部服务接口发生变化时,或许修改一处就能够满足变化
4、如果要对传输层、安全进行定制时,会非常方便
【参考资料】
模块分解原理的探索
Interface Design — Best Practices in Object-Oriented API Design in Java
Part 1: Exploring the development, interfaces, and operation semantics of services
Part 2: Exploring the development, interfaces, and operation semantics of services
SOA模式短文推荐
强烈推荐SOA爱好者下载收藏《SOA Pattern》一文,又有精品全文《SOA Design Pattern》推荐下载

作者将模式分为三类:(1)基本服务模式,是最小粒度的服务模式,已经无法再次拆分,可以认为是模式原语,多用在服务层面使用。(2)架构模式,通用的SOA系统设计要素,多用在系统设计时,做为架构风格的一个元素。(3)复合模式,基本服务模式的组合,用来定义系统的衔接特征,多用在应用系统整合场景。下面按照自己的理解和使用经验,简单说明一下,具体的介绍还请研究原文。
基本模式说明:
- Aggregator:将多个独立的消息体通过该计算组件组合为一个消息体。
- Service Bus:将多个独立的系统(已有系统、新系统)通过统一机制整合起来。
- Dynamic Routing:与规则库配合,实现一个消息体的路由,实现上常采用基于消息头路由、基于内容路由2中情况。
- Event-Driven Consumer:常用在资源有限的场景,解决资源竞争的问题,例如,网络接入时,系统所能接收到socket有受限的,此时即可通过该模式解决。
- Filter:消息内容的过滤,例如,关键字过滤,也可用在信息补全场景。
- Router:将消息发送到不同的应用,与Dynamic Routing的区别在于,此时规则是固定的,常常有2中使用方式,单播、多播。
- Translator or Transformer:用在消息格式的转换场景。
架构模式说明:
- Asynchronous Processing:异步事件风格解决服务之间的交互,例如,ATM到银行账务系统,多用该模式。在实践中,也鼓励使用这种风格,实现SOA,以弱化一致性、事务性,缓解资源受限等场景,该模式需要业务配合才能完全发挥。
- Bridge:常用在2中体系之间的交互,把一种协议转换为另一种协议。它与Translator的区别在,Translator是针对消息的,它针对链路。
- Cross-Service Operation:把多个服务封装为一个服务来使用,以确保一致性、事务性,可以把该风格弱化为分布式事务风格。
- Event-Driven Dispatching:基于事件的派发,常用在pub/sub场景。
- Process Aggregation:把多个过程聚合在一起,按照一定顺序进行消息处理,此时不要求一致性,每个过程都是独立的,用BPM技术更容易理解该场景。
- Routing and Filtering:实现一个消息的多种渠道应用,例如,一个业务通知,通过Mail,IM、SMS、语言、甚至特殊应用传达给用户的场景,一个产品为多个渠道提到内容的场景。
- Replicator:实现一份消息,服务处理的时候,同时往数据库复制一份,例如,数据备份容灾、数据采集、数据审计等。
复合模式说明:
- Centralized Schema:解决跨应用边界的数据共享问题。
- Concurrent Contracts:解决一个服务多个消费者时,每个消费者接口不同的问题。
- Decomponse Capability:??
- Enterprise Service Bus:通过一个消息总线,解决应用整合的问题,化解复杂的网状链接。
- Fault-Tolerant Service Provider:通过负责均衡解决服务高可靠性的问题。
- Wrapper:解决传统应用服务化的问题。
ESB产品Mule+Spring实现最简领域驱动框架
| 更多内容在: |
架构专栏 |
先发一个消息,开源ESB产品Mule又发布了新的LDAP组件,很是让人佩服,首先赞一个先。
架构风格的基本组成体:计算组件、连接组件、信息、数据、配置。下图中,又抽象了一个领域组件,这也是为了让这个风格表示法能够同样适用于业务层面。

MULE+Spring完全支撑上图所示的架构风格,我们可以很容易的用它们实现最简领域驱动框架,以支持领域驱动设计。MULE大量提供连接组件(通信,协调,仲裁等)和信息链路拦截机制(可使用格式转换,加密,压缩之类的),也提供一些通用的计算组件,但更多的通用计算组件还是依赖自己开发;Spring提供了配置能力,把组件之间的关系记录下来;唯独留下新引入的领域组件(领域组件的计算能力可以很容易通过连接组件实现与其它组件的交互),需要我们自己来开发。
在《领域驱动设计在SOA架构中的实践》一文中提到技术架构师解决如下问题:“解决缓存、事务管理(包括分布式事务)、分布式接口规范、通信机制、共性系统服务的注入和应用契约、领域驱动设计风格与编程模式的确定、测试方案的确定、工具的提供、搭建可运行的环境、跨环境(分布式环境)的服务依赖策略、模型转换策略和工具的提供、集成策略”。下面用Mule+Spring的组合来对应一下:
- 缓存:是一个计算组件,需要单独开发。
- 事务管理:是一个计算组件,利用Spring事务,分布式事务需要单独定制一个计算组件。
- 分布式接口规范:需要单独定义。
- 通信机制:MULE提供。
- 共性系统服务的注入和应用契约:Spring提供。
- 领域驱动设计风格与编程模式的确定:见实践一文。
- 测试方案的确定:Spring提供单元测试,Mule提供分布式测试,可以定制一些测试辅助组件,例如:数据生成组件,该组件配合MULE的定时组件,很容易实现简单的自动测试。
- 工具的提供:MULE,Spring都提供了工具,DAO生成组件依据存储框架的不同寻找合适的。
- 搭建可运行的环境:再搞一个JBOSS,整个环境OK。
- 跨环境(分布式环境)的服务依赖策略:见实践一文,Spring + Mule提供技术支持。
- 模型转换策略和工具的提供:用Dozer 解决Bean-Bean,JAXB解决Bean-XML,或者Hessian解决Bean-Hessian;开发这些计算工具,配置在Mule的链路中,即可使用。
- 集成策略:Mule提供了各种连接组件,轻易集成外部系统或服务。
- 额外的:Mule提供了分布式计算环境,可以作为SOA技术平台来使用。
- 额外的:Mule提供了服务模型,可以作为服务平台来用。
- 额外的:Mule提供了Galaxy,解决服务治理。
通过上面的匹配,就会发现,以MULE+Spring为基础,实现一个更强大的领域驱动框架将会非常容易。
领域驱动设计在SOA架构中的实践
| 更多内容在: |
架构专栏 |
本着实践–阅读–思考–再实践的原则,总结一下领域驱动设计在SOA架构中的实践,关于领域模型设计自身,我建议大家阅读《领域驱动设计》一书,关于领域驱动实践的入门教程,我强烈建议阅读《领域驱动设计和开发实战》;关于领域驱动实践的再一个实践入门,我建议阅读《Domain-Driven Design in an Evolving Architecture》。2篇文章中,作者还提供了架构图,它们已经能够满足大部分应用的架构需求。
|
|
1.为什么要在SOA架构中使用领域驱动设计?之所以采用SOA,不是因为好玩,而是业务复杂性导致的,迫不得已而为之呀(可参考《SOA、服务化》栏目内的一些文章)。使用了SOA架构,不代表业务复杂性自动消失,还是从业务入手分析之,从目前广泛流行的方法学中,领域驱动设计完全符合符合这个需求,它就是为解决业务复杂性而生的。
2.职责分离。有时候,由于资源的情况,会让一个人同时承担技术架构和业务系统分析的角色,这种情况下,往往会搞的这个领域驱动设计乖乖的,简单玩玩也是无所谓了,对于较大的互联网公司,就是比较头疼的事情了,应用系统的开发者不同,这架构风格绝对会千奇百怪。因而还是要把这职责分清楚,搞一个技术架构师,再搞一个业务系统分析师。
- 技术架构师:关注与领域驱动设计在SOA架构中的技术部分,重点在“解决缓存、事务管理(包括分布式事务)、分布式接口规范、通信机制、共性系统服务的注入和应用契约、领域驱动设计风格与编程模式的确定、测试方案的确定、工具的提供、搭建可运行的环境、跨环境(分布式环境)的服务依赖策略、模型转换策略和工具的提供、集成策略”。这些东西确定下来之后,技术架构风格就完全定型,下面的工作就是把架构风格“固化”,此时有2中策略,固化为框架、固化为产品,建议在头2个项目迭代中,以框架形式提供,之后就可以考虑以产品方式提供(可参考通用技术系列文章)。有了“领域驱动”在SOA架构中的支撑框架(产品),将大大的简化业务系统分析师的工作,但距离成功还有70%的距离(只有业务成功了,才是成功),可这30%将占据80%的质量属性,严重影响全局SOA架构体系的成功。
- 业务系统分析师:关于业务,关注领域驱动设计,请千万忘记技术,相信我们的战友会解决这些问题的。
3. 领域驱动设计概念架构风格图A,该风格比较保守一下,在具体cache的使用上,有2中选择,或者领域中使用,或者仓储中使用,具体那种方式适合,要看领域模型和缓存需求,不是啥都适合通过仓储缓存。

4. 领域驱动设计概念架构风格图B,该模型的变化之处在增加了一个事件层。好处:领域模型与仓储模型、cache模型等隔离,同时提供了扩展机制;缺点:不如风格图A那么直接,容易理解,且事件层放在这儿有点不伦不类。在使用中注意几点:事件对象可以业务域统一一个,或者技术框架层统一一个;建立事件标识契约,例如领域对象的类型;保证事务,避免跨线程。

5.注意以下几点:
- 领域切分不要太小,切不可变成简单的分布式方法的调用。
- SOA重用的是服务,不是功能。
- 服务应该与需求用例对应;服务提供的方法与设计用例对应(设计用例是对需求用例的实现)。
- 领域内部的复杂度与业务能力体系相关,领域不是平面的、是树状的、是有层次的,可以用层模式来切分领域对象。
- 领域内部既有用户看见的东西,也有用户看不见的业务内能力,对外暴露的值对象信息,往往来自最上层的领域对象。
- 即使一人身兼2职,也要把自己切分开,搞技术架构时,忘记具体业务,把架构风格固化下来,请开发程序员按照这个编程模型来开发;搞领域驱动设计时,完全忘掉技术。
- 如果作为公司的架构师角色进入项目团队,那么请:走入程序员中间,走入项目中间,但不要走入业务编码中间;到这儿是获取需求,验证框架(验证架构风格),发现问题的;目标是整个SOA架构的成功,不仅仅是项目的成功。
- 服务对象层,请参考《SOA实践之:Java服务接口设计的一些实践准则》,务必形成严格的编程规范,强制遵守。
SOA是可持续的战略,不是一次项目
常常听到如下关于SOA的说法:
- 我们完成了SOA化改造。
- 我们的SOA化,已近实现了80%。
- 这次项目是为了实现SOA化,因此要请有这方面经验的公司来实施。
- 从技术角度层面来说,我们达到了SOA化的目标。
- 这是我们最后一个SOA个项目
- 等等
如果这些话出自程序员,项目经理等人员,也是无所谓的;如果出自IT管理层的人员,哪就是一种可怕的想法了。因为他把SOA仅仅看作是一次项目了,而没有把它作为战略。这样的后果也是可以预见的,最终本次投资将会失败。
因此,建议那些准备实施SOA的公司,首先回答这个问题:SOA是贵公司的一个战略吗?
为什么要回答这个问题?
- 从SOA概念来看
- 从SOA目标来看
- 从IT管理角度来看
- 从系统分析角度来看
- 从技术特征来看
关于什么是SOA,我有几篇文章,可以参考(《这些都不是SOA》,《ESB产品升级准备:SOA、ESB、JBI、SCA、OSGI概念再学习、再理解》,《SOA,想说爱你不容易》)。丛概念上可以看出,SOA不少技术问题,它是一种思想,一种架构,因而不可能通过一个项目就把这种思想或架构完全实现;思想也是会发展得,今天的思想在明天或许就会不适应。
SOA的目标是解决业务敏捷性问题。如今,大环境复杂多变,任何一个公司都不敢说自己的业务就是这几个、自己的业务会一直不会变,很多企业,在很短的周期内,就会推出各类产品(特别是服务类企业,例如:银行、互联网企业等)。只有那些能够快速适应变化,主动寻求变化的企业才能够生存下来,发展起来,例如:会跳舞的大象IBM,IT行业最大的特色就是变化快、发展快,如果IBM的业务不能敏捷的适应这种变化,它还能跳舞吗?正因为IBM深刻理解了SOA的目标,因而它把SOA看作一种战略–可持续的战略。
对于IT主管,一定要确保本公司的IT战略是可持续的,不能今天一种想法,明天又看到什么好思想,就换了一种想法,或者换个主管,就彻底换了战略(这种做法,就像我们国内的城市建设,路总是修不好)。既然从一种思路切合到了SOA领域,那么再次切换的成本将会更高,也不可能多种思路同时进行(吃得多,嚼不烂)。此外,谁能保证一次项目就确保几年内业务变化的需要?从我们实施的经验来看,必须持续的进行SOA化,才能确保SOA持续的发挥效果。
每次项目都要进行系统分析,都是基于当前的业务进行,那么不可避免的就会有一些盲点(从认知的角度看,人不可能一次就抓住事物的本质,何况大多数系统分析员不具有这种能力)和不合理支出。这些问题,只有在实施后一段时间才能发现(我们自己的实践经验);此外随着补丁的实施,原有的系统已经恶化,和最初的SOA化目标越来越远。此时,如果不能再次用SOA的思想重新审视系统,就将彻底失去了SOA带来的好处。
SOA非但不能让系统架构变得简单,反而变得复杂了(系统设计要求变高了、系统部署复杂了、发布复杂了、各类SOA相关技术的引入增加了对人员的要求),这也是为什么要引入SOA治理。从这一点来看,只有持续的SOA化,才能最终发挥SOA技术架构的好处。
再次提醒将要进行SOA化的朋友和已经进行了SOA化的朋友,把SOA当作贵公司的可持续的战略来看,而不要认为它紧紧是一次项目。
SOA实践之:Java服务接口设计的一些实践准则
在SOA领域,一些OO领域的接口设计原则具有很好的借鉴意义,但随着SOA化带来的分布式特性,有些东西就需要进行特殊处理。下面就我们进行SOA化过程中接口设计过程中,曾经比较容易出现过问题的地方进行部分总结。
【术语说明】
-
服务与业务组件的定义:
- 业务组件:最小的业务逻辑单元,例如:密码验证组件、登录组件。
- 服务:业务模块提供给外部的核心业务逻辑,服务组合业务组件。例如:修改密码服务,会使用密码组件的验证服务,也会使用登录组件的修改密码服务。
【说明:】可否密码验证组件的功能合并到登录组件哪?可以的,此处之所以单独分离,与我们提供多种密码验证机制有关,登录组件对应的业务并未完全涵盖密码验证组件的功能。
- 服务接口的方法,禁止重名(overload特性)。大多数序列化工具【例如:XFire】对重名方法进行处理时,都不可能避免的存在或多或少的问题。为了减少麻烦,还是放弃OO的这一特性,或者在面向服务领域,重载本身就不是一个特性。
- 服务接口的方法,避免返回异常【我们是禁止异常,包括业务异常、系统异常】。这个要求看起来违反了一些对象设计的原则,从实际使用角度来看,具有如下好处:很多工具对异常序列化很容易出现错误;一不小心,外部使用者还会产生异常依赖;需要外部使用者自己把异常翻译为业务语义【从业务角度来说不是件好事情】。
- 服务接口必须有清晰的文档说明,对于常量性质的参数,要列出支持的常量值。
- 服务接口尽可能把查询和业务方法分离【非必须】。好处是使用AOP时简单方便,对于查询方法进行特殊处理时也比较方便,例如:与cache整合、与搜索引擎整合、与特殊权限要求进行整合(例如,对返回信息按照权限进行过滤)等。
- 服务接口参数排列按照如下原则:操作主体对象或ID,业务参数{…},环境参数。
例如:verifyPassword(String operatorId, String password, ServiceContext serviceContext)
operatorId是操作主体的ID。
Password是密码。
serviceContext是环境参数。 - 业务处理类接口,返回值可以采用统一对象模式。例如:ServiceResult:
public class ServiceResult<T> implements Serializable {
private static final long serialVersionUID = 760567785564620157L;
private boolean isSuccess;
private String resultCode;
private T resultObject;
public boolean isSuccess() {
return isSuccess;
}public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}public String getResultCode() {
return resultCode;
}public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}public T getResultObject() {
return resultObject;
}public void setResultObject(T resultObject) {
this.resultObject = resultObject;
}
}
采用这种模式,实际是按照协议的角度进行设计,所有服务按照统一的包装模式返回。 - 服务接口的参数避免使用Object类型,集合类型要指定具体对象类型。原因:当使用Object时,使用者获取到WSDL文件时,不知道该传递什么业务数据,如果是其它语言的调用者,更无法理解了;这类接口设计违反了业务SOA的原则,暗含着如下潜台词:业务上不明确的,不清晰的。
- 服务接口避免使用集合类型,尽可能使用数组类型。
- 服务接口中使用枚举的原则:如果是对外界发布,避免使用枚举;如果使用者众多,避免使用枚举;如果是内部几个专用系统使用,可以考虑使用枚举,可以参考《枚举产生的系统耦合性》。【文档的清晰化,可以化解常量参数不明确的问题】
- 服务接口避免设计成面向数据的操作,例如:updateStatus。这类接口,通常包含太多的业务含义,甚至没有业务含义,需要外部使用者去理解业务内部的细节,否则无法进行操作。这也是我们设计服务接口时,需要特别注意的地方,不要让外部使用理解业务内部是如何存储的,结构是什么样的,这样会导致业务耦合度过高,服务内部发生重构,变化时,外部使用也需要进行变化。
- 服务接口层建议设计一个统一的模板框架,来处理性能分析、特殊参数处理(例如:ServiceContext)、事务处理、异常处理、日志处理、事件处理、审计处理等具有共性的行为。
- 服务接口的方法名避免以:is,get开头,用find,check等来代替之,避免Java序列化工具把该类当作Java Bean进行处理。
- 业务组件中异常的要求:所有的业务组件,如果发现违法业务原则的地方,需要中断程序的执行,那么throw统一业务异常,该业务异常可以借鉴如下方式设计:
public class ComponentException extends Exception {private static final long serialVersionUID = -6900280471536579015L;
private ErrorCodeEnum code = null;
public ComponentException ErrorCodeEnum code) {
super();
this.code = code;
}public ComponentException ErrorCodeEnum code, Throwable e) {
super(e);
this.code = code;
}public ErrorCodeEnum getCode() {
return code;
}public void setCode(ErrorCodeEnum code) {
this.code = code;
}
}坏处:从异常名看不出明确的业务语义。
好处:避免服务层需要捕获多种异常;业务语义通过ErrorCodeEnum返回到业务层。 - 业务组件内部尽可能少的进行事务处理【除非有特殊的需求】、异常处理【除外业务组件自身组合了外部的业务服务,此时需要处理异常】。
好处:统一由服务层进行处理,可以避免事务处理的错误。不进行异常处理,可以避免异常转义时,出现信息丢失;此外,如果使用了统一异常,那么就更不能在业务组件内部进行异常处理了,否则会发生原始业务限制规则被丢失的可能。
对于一些基本原则,可以参考《SOA实践之:在程序层,面向服务的设计准则探讨》一文。
使用方,推荐使用代理模式来使用外部服务。
1、可以进行一些特殊处理,例如:网络异常处理,远程性能评估等
2、对内部提供重业务层面的方法,屏蔽环境参数
3、外部服务接口发生变化时,或许修改一处就能够满足变化
4、如果要对传输层、安全进行定制时,会非常方便
【参考资料】
模块分解原理的探索
Interface Design — Best Practices in Object-Oriented API Design in Java
Part 1: Exploring the development, interfaces, and operation semantics of services
Part 2: Exploring the development, interfaces, and operation semantics of services