领域驱动设计实践@瑞友科技IT应用研究院池建强Twitter:@sagacityWeibo:@池建强Mail:jackychi@gmail.comAboutME•池建强,70后程序员,98年毕业,先后就职于洪恩软件、RocketSofeware和用友集团-瑞友科技,现任瑞友科技IT应用研究院副院长•先后从事互联网和企业应用开发,目前致力于基础应用平台的研究•热爱技术和编码工作,坚持年轻时的理想,倒霉的乐观者•技术领域:Java、Python、Ruby、Objective-C、DDD、OSGi、AppPlatformDomain Model -‐ What Why How?模型是抽象的现实是形象的技巧是重要的思想是永恒的从软件系统诞生之初,程序设计者就开始梦想有⼀一天能够像建造桥梁和房屋那样“透明”的构造软件,让现实和代码对应起来软件开发总是比任何人估计的慢,如果不是有意的放宽项目期限,几乎不会出现提前完成的软件项目和产品。有时候我们必须耐下心来把所有琐碎的、困难的、细节的问题都解决并不断打磨,才能完成,有时候没有别的办法,只能等待⼀一个案例2010-012010-032010-052010-12......采用领域驱动的方式开发幸福的时光总是短暂的......2010-012010-032010-052010-12......商务要求尽快出⼀一个版本2010-012010-032010-052011-04......好日子结束了苦逼的总是开发者采用领域驱动的方式开发领域驱动设计不是用来解决开发速度的领域驱动设计是用来解决软件核心复杂性的软件系统面向对象的设计思想可谓历史悠久,20世纪70年代的Smalltalk可以说是面向对象语言的经典,直到今天我们依然将这门语言视为面向对象语言的基础面向对象是大部分语言的⼀一个基本特性,像C++、Java、Objective-C这样的静态语言,Ruby、Python这样的动态语言都是面向对象的语言面向对象的分析设计2004年著名建模专家EricEvans发表了他昀具影响力的书籍《Domain-DrivenDesign》——TacklingComplexityintheHeartofSoftware中文译名:领域驱动设计—软件核心复杂性应对之道,书中提出了“领域驱动设计(简称DDD)”的概念。领域驱动设计事实上针对是OOAD的⼀一个扩展和延伸,DDD基于面向对象分析与设计技术,对技术框架进行了分层规划,同时对每个类进行了策略和类型的划分采用DDD的设计思想,业务逻辑不再集中在几个大型的类上,而是由大量相对小的领域对象(类)组成,这些类具备自己的状态和行为,每个类是相对完整的独立体,并与现实领域的业务对象映射。领域模型就是由这样许多的细粒度的类组成领域驱动设计是技术人员和领域专家沟通的桥梁技术人员和领域专家沟通的桥梁是领域驱动设计技术人员领域驱动设计沟通领域专家桥梁public interface IBookService { double submitOrder(int orderId);}不容易理解的抽象ûpublic interface IBookService { double submitOrder(Order order);}ü抽象-打折策略领域模型和事务脚本事务脚本(TransactionScript)的核心是过程,每个过程处理来自表现层的单个请求。大部分业务应用都可以被看成⼀一系列事务,从某种程度上来说,通过事务脚本处理业务,就像执行⼀一条条Sql语句来实现数据库信息的处理事务脚本(TransactionScript)的核心是过程,每个过程处理来自表现层的单个请求。大部分业务应用都可以被看成⼀一系列事务,从某种程度上来说,通过事务脚本处理业务,就像执行⼀一条条Sql语句来实现数据库信息的处理事务脚本把业务逻辑组织成单个过程,在过程中直接调用数据库,业务逻辑在服务(Service)层处理Action处理UI层的动作请求,将Request中的数据组装后传递给BusinessService,BS层做简单的逻辑处理后,调用数据访问对象进行数据持久化,其中VO充当了数据传输对象的作用,只具备getter和setter方法,没有状态和行为TransactionScript效果?后果?看代码事务脚本模式的特点是简单容易理解,面向过程设计。事务脚本模式的特点是简单容易理解,面向过程设计。对于少量逻辑的业务应用来说,事务脚本模式简单自然,性能良好,容易理解,而且⼀一个事务的处理不会影响其他事务。事务脚本模式的特点是简单容易理解,面向过程设计。对于少量逻辑的业务应用来说,事务脚本模式简单自然,性能良好,容易理解,而且⼀一个事务的处理不会影响其他事务。不过缺点也很明显,对于复杂的业务逻辑处理力不从心,难以保持良好的设计,事务之间的冗余代码不断增多,通过复制粘贴方式进行复用。可维护性和扩展性变差。SmartUI-反模式——在用户界面中实现大部分业务逻辑领域模型(DomainModel)‣面向对象分析和设计‣领域模型具备自己的属性行为状态,并与现实世界的业务对象相映射‣各类具备明确的职责划分,领域对象元素之间通过聚合和引用等关系配合解决实际业务应用和规则‣可复用,可维护,易扩展‣可以采用合适的设计模型进行详细设计‣要求设计人员有良好的抽象能力DomainModel我们也来看代码在实际的设计中,我们需要根据具体的需求选择相应的设计模式。具备复杂业务逻辑的核心业务系统适合使用领域模型,简单的信息管理系统或大数据处理可以考虑采用事务脚本模式领域驱动设计元素•成熟、清晰的分层架构•领域对象与现实世界的业务映射•明确的职责划分分层架构•领域对象是核心•领域对象复用:完整的业务对象描述•设计复用:设计基于领域对象而非数据库复用•具备复杂业务逻辑的软件开发•对设计和开发人员要求较高•不适用普通CRUD的业务•软件的维护性和扩展性良好使用场景分层架构名称职责展现层负责向用户展现信息以及解释用户命令应用层很薄的⼀一层,用来协调应用的活动。它不包含业务逻辑。它不保留业务对象的状态,但它保有应用任务的进度状态领域层本层包含关于领域的信息。这是业务软件的核心所在。在这里保留业务对象的状态,对业务对象和它们状态的持久化被委托给了基础设施层。基础设施层本层作为其他层的支撑库存在。它提供了层间的通信,实现对业务对象的持久化,包含对用户界面层的支撑库等作用实体,值对象,服务En#ty(实体)— 人/座位-‐先到先得?Value Object(值对象)—画笔/地址name*nameServices(服务)Entities:具备唯⼀一ID,能够被持久化,具备业务逻辑,对应现实世界业务对象Entities:具备唯⼀一ID,能够被持久化,具备业务逻辑,对应现实世界业务对象Valueobjects:不具有唯⼀一ID,由对象的属性描述,⼀一般为临时对象,可以用来传递参数或作为实体的属性,也可以封装复杂的计算逻辑Entities:具备唯⼀一ID,能够被持久化,具备业务逻辑,对应现实世界业务对象Valueobjects:不具有唯⼀一ID,由对象的属性描述,⼀一般为临时对象,可以用来传递参数或作为实体的属性,也可以封装复杂的计算逻辑Services:为上层建筑提供可操作的接口,负责对领域对象进行调度和封装,同时可以对外提供各种形式的服务领域模型的生命周期活动状态数据库数据库或文件创建存储重建归档删除删除修改关系数据库面向对象数据库NoSqlFactory和Repository基于Aggregate,对特定生命周期转换的复杂性进行了封装Aggregate(聚合)什么是聚合,我们来画⼀一下Aggregate rootItemProductIPaymentStrategyCashPayCreditCardPayFactory -‐ IoCbeans!--Volvo是ICar的实现类--beanid=vcarclass=Volvo/bean!--为驾驶者提供用车,依赖注入--beanid=oneDriverclass=Driverpropertyname=carrefbean=vcar/ref/property/bean/beanspublicDriver{privateICarcar;publicDriver(){}publicvoiddrive(){car.method();}//getterandsetter}Driverdrive=helper.getBean(oneDriver);drive.drive();Repository‣操作持久对象并管理其生命周期‣领域对象和持久化解藕‣封装持久化技术‣多数据源、连接池、数据源访问代理‣O/RMapping技术‣NoSql‣分布式文件系统‣持久化层可替换来自QCon淘宝的分享规则引擎的案例领域元素‣Package——规则包‣Rule——规则‣Attribute——规则属性‣When/Then——条件‣......如何实现这样⼀一个设计呢?看例子吧⼀一些原则表意接口Inten#on Revealing Interface如果⼀一个开发人员为了使用⼀一个组件必须要去研究它的实现,那么就失去了封装的意义无副作用函数Side-‐Effect-‐Free Func#on所谓副作用(sideeffect),指的是函数内部与外部互动(昀典型的情况,就是修改全局变量的值),产生运算以外的其他结果——函数式编程?所谓副作用(sideeffect),指的是函数内部与外部互动(昀典型的情况,就是修改全局变量的值),产生运算以外的其他结果——函数式编程?利用VO的不变性,创建无副作用的函数,例如,传入或返回的对象是byvalue,而不是byreference,对返回值的修改,不会影响原来的对象。ConceptualContour概念轮廓通过坝或其他手段把⼀一个湖分割为几块,在任何⼀一块中投入石子都不会影响其他部分。把设计元素(行为、接口、类、聚合等)分解为内聚单元。通过重构,找出模型中经常变化的部分和基本稳定的部分。如何实现组件之间的解耦ESB也是⼀一种方式,但是远程调用?事务?设计时需要慎重选择小结⼀一表意接口把对象描述成有意义的单元。无副作用函数让我们可以安全的使用这些单元。概念轮廓让这些单元更稳定,更符合直觉,更容易组合小结二低耦合是对象设计的⼀一个基本要素,尽可能保持低耦合。把其他所有无关的概念提取到对象之外。消除所有不重要的依赖。限制交叉依赖的方法:1、子系统2、模块化3、聚合4、单向关联领域模式Specifica#ons‣规约是⼀一种布尔断言‣规约是业务规则的⼀一部分‣理论上规约类中的方法只有⼀一个:IsSatisifedBy(Object)‣规约用来测试对象是否满足某种条件,用来进行对象查询,也可以作为某个对象的创建条件‣单⼀一规约规则。多个规约通过组合表现复杂的规约publicclassBookShelf{privateListbooks;publicBookShelf(){books=newArrayList();}publicvoidaddBook(Bookbook){books.add(book);}}增加规约:1、不同作者的书放到相应的书架2、不同类型的书放到相应的书架Specification引入规约publicclassAuthorSpecificationimplementsSpecification{privateStringauthor;publicAuthorSpecification(Stringauthor){this.author=author;}publicbooleanisSatisfiedBy(Bookbook){return(book.getAuthor().equals(author));}}publicvoidaddB