2015-11-11 17:37:41 u010870518 阅读数 17062

背景

使用DDD开发大概也有五个月的时间了,由于当时公司导师的推荐,第一次接触DDD领域驱动到现在彻底迷恋这种开发的模式,为其思想的奥妙所折服,一直以来,总想花一点时间来总结一下,正直光棍节(天猫狂欢购物节)当天,“静下心来”(PS:没有人民币)总结一下。

说起DDD不得不说一篇文章:http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html

第一次接触,就是这篇文章,当时看起来晦涩难懂,后来慢慢的读起来,每一次都有精神的提升,网上也有很多关于DDD的介绍,这里将不会在介绍概念什么的,下边主要是自己工作过程中的一些总结。

如何快速入门

第一次接触DDD的时候,概念高深莫测,奥秘深不可见,大有不知所云的趋势,后来导师的引导,让直接从项目中直接入手,遂逐步揭开其一层层雾纱,如图1:

这里写图片描述

DDD一般的分层结构和调用顺序如上所述,infrastructure是基础设施层,domain是领域层,application是应用层,facade和facade-impl是门面层(前者是门面接口层,后者是门面实现层),webapp是用户接口层(采用web形式)。

下边是一种项目的分层图,采用的Maven管理代码。图2:

这里写图片描述

分层的介绍

1、web:首先包含网站前端,如果使用SpringMVC的话,还需要其Controler,

这里写图片描述

该Controler层主要是用于接收HTTP请求和返回客户端,一般不进行逻辑上的判断,其代表了用户可以进行的操作,一般不涉及领域驱动的思想,示例代码:

@ResponseBody
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public String create(AirlineWhiteListDTO airlineWhiteListDTO) {
        MDC.put(ConstString.TRACE_ID, LogUtil.getTraceId(ConstAirlineWhiteList.CREATE));
        Response response = airlineWhiteListFacade.create(airlineWhiteListDTO);
        return super.handleResponse(response);
    }

第一行使用MDC用于记录(打log)http请求的顺序和调用的方法,第二行调用“门面层”facede,获得Response返回对象,第三行用于判断是返回界面还是返回data等操作,即时SpringMVC的ModalAndView;

2、facade和facade-impl一个是门面层接口一个是门面层接口的实现类,示例代码:

@Autowired
IAirlineWhiteListApplication airlineWhiteListApplication;

public Response create(AirlineWhiteListDTO airlineWhiteListDTO) {
        Response response = new Response();
        try {
            AirlineWhiteList airlineWhiteList = AirlineWhiteListAssembler.toEntity(airlineWhiteListDTO);
            airlineWhiteListApplication.create(airlineWhiteList);
        } catch (Exception e) {
            response = new Response(e);
        }
        return response;
    }

可以看出这是在web的controller中访问的方法,主要进行数据的组装(实体对象和数据传输对象的转换),以及返回对象Response的转换。

之所以成为门面层,我们暂可认为是我们用户可以看到的整个系统的东西,例如上述中的Response就是返回给用户看的东西。

3、Application层,上述的facade调用到了application中的方法,

@Inject
private AirlineWhiteList airlineWhiteList;

public boolean create(AirlineWhiteList airlineWhiteList) {
        return this.airlineWhiteList.add(airlineWhiteList);
    }

这一层主要是操作实体对象的,是于数据更近一层的操作,主要定义了用户所拥有的方法和属性,操作实体对象层,将实体对象所有的方法展示出来供用户使用;

4、domain层,数据实体层,相比MVC中的modal简单的只是数据库的映射,这种“毫无灵魂”的对象,领域模型中不但有一个实体对象的属性还有其方法,(我们可以在实际使用的时候使用继承DTO的方式),在这一层中定义了实体对象操作数据库的方法;

这里我们的实体对象不仅是拥有属性还具有方法的,就像一个人一样,我们不但拥有做人的基本特征(两手、两脚等),我们还有属于自己的技能(方法),这样的话才是一个拥有灵魂的东西;

5、infra这一层包含了访问数据库的方法、数据仓储(sql、nosql、api)和一些工具类,service等;

由于是domain层调用该层的,实体对象的操作固然包含CRUD,既是我们需要进行对数据库的操作,当我们只有一个数据源的时候,很简单,但是后期项目中数据源可能会增加,可能会添加缓存等,这样的话使用原来的MVC模式的话,我们可能需要修改很多,如下图:

这里写图片描述

但是使用DDD的话,由于我们的Domain调用的是infra 数据仓库Repository接口,Repository中定义了访问数据源的方法,这样的话,当我门新增数据源的时候,我们的Domain层以上都无需修改,只需进行infra层的修改即可。
这里写图片描述

上图中有2个数据源,一个是sql一个是nosql,在方框中的Repository即调用的是nosql和sql中的方法,这样的话Domain直接调用Repository即可。

领域驱动的示例:

如果想快速的领回DDD的奥妙,这里有一个案例,一个很不错的开源系统,使用的正是DDD思想,可以把代码下载下来仔细研究,其中的思想是很不错、很不错的,地址如下:http://www.openkoala.org/

总结:

类比MVC“哑铃式”的分层结构中,Model和View代码少,Controller代码臃肿的布局格式,DDD拥有更多的分层,各层之间各司其职,协调工作,一步步调用,井然有序,对于后期的维护,只要是熟悉DDD的开发人员都可以很好的维护。

2013-01-29 16:43:58 iteye_11133 阅读数 55

最近花了一点时间了解Sculptor-一款面向领域模型开发利器,发现其设计理念和功能实现相当不错。以面向模型驱动开发的方式,将DDD的概念和模式运用于DSLDomain Specific Language)之中,并为其预置了HibernateSpring代码框架实现,并且内置很多扩展性特点,完全区别于以往普通的代码生成器,使得开发者更加关注与需求功能实现,而摆脱技术框架的束缚,大大提高了开发效率。

建议对领域模型驱动开发感兴趣的同学都了解一下,官网地址如下:
http://fornax.itemis.de/confluence/display/fornax/Sculptor+(CSC)

本文抛砖引玉摘取官网部分wiki内容进行介绍。

概述

Sculptor是一个简单而强大的代码生成平台,提供了基于MDSDModel Driven Software Development)的快捷通道。当你使用Sculptor时,你可以精力集中于面对业务领域建模,而替代关注技术实现细节的传统方式。你可以将来自于DDDDomain-Driven Design)的概念抽象描述到DSLDomain Specific Language)原始文本文件中,Sculptor 将使用XTextXpand去解析该DSL文件,并生成高质量可用的Java代码和配置文件,这些生成的代码都基于著名的业界框架,如SrpingHibernateJava EE

 

 

 

上图描述了开发人员如何使用DSL描述应用,使用Maven生成代码和配置文件。生成的代码和手工编写的代码很好的进行了分离。手工编写的代码,如Junit测试用例、业务逻辑等将添加到子类或者其他定义良好的位置中。

在开发过程中的DSL和代码生成驱动并非昙花一现,而是在整个过程迭代反复,可以和TDDTest Driven Development)结合并随设计演讲,正如在Test Driven Development with Sculptor索引中的解释。

Sculptor亮点介绍:

ü  运用DDD的概念在DSL原始文件中直接描述编程语言,如:ModuleServiceEntityValue ObjectRepository

ü  HibernateSpring提供了最佳实践。目标环境是一个真正可用的商业系统。它很容易实现具备CURD的服务层,但这样的设计假定还需比简单的CURD设计(如灵活性、业务逻辑等);

ü  Sculptor的设计和代码生成机制比目前流行的生成工具(如Hibernate synchronizeHibernate reveng)更加完善,可以即开即用。或许最大的优势便是可以通过一个独立的模型生成一套完整的应用程序,而不是一个需要全面进行设计的代码片段;

ü  快速上手。最初的MDSD起步可能会很臃肿,但Sculptor减少了很多比必要的枝节;

ü  Sculptor并不是万能的工具,但足以适合大多数系统开发,而定制化的内容迟早也将要面对。Sculptor的设计和文档还在不断演进,最终的结果将会应你的需求更加易用。

 

快速起步

这里将使用一个例子的实践说明Sculptor。这个例子完整的介绍还没有完全在用户手册中描述,但可以在SculptorWiki中找到。

这个例子是一个电影和书籍的library管理系统。这个系统的核心业务模型如图所示:


 

Maven Archetype

通过Maven运行一些简单的命令就可以为应用程序生成Eclipse工程,Sculptor提供了这样的Maven Archetype去面对这些工作。

Domain Specific Language(DSL)

一个基于Sculptor的应用被描述于一个DSL原始文件中,DSL文件是一个最为原始文本文件代码,可以进行查找、复制、粘帖和合并等特性,SculptorDSL提供了Eclipse编辑器,可以支持高亮显示错误、代码检查和概览。

在刚开始Library例子中,DSL对模型的描述如下:

Application Library {

basePackage = org.library

 

Module movie {

Service LibraryService {

findLibraryByName delegates to LibraryRepository.findLibraryByName;

saveLibrary delegates to LibraryRepository.save;

findMovieByName delegates to MovieRepository.findByName;

}

Entity Library {

String name key

reference Set<@Movie> movies

Repository LibraryRepository {

save;

@Library findLibraryByName(String name)

throws LibraryNotFoundException;

protected findByCriteria;

}

}

Entity Movie {

String title not changeable

String urlIMDB key

Integer playLength

Repository MovieRepository {

List<@Movie> findByName(Long libraryId, String name)

delegates to FindMovieByNameAccess;

}

}

}

}

 

这里DSL定义了两个模型和一个服务逻辑。它可以定义类似的模型,包括模型的属性和应用。

DSL的核心概念源自于DDD,如果你还没有对DDD没有认识,那么可以下载《DomainDrivenDesignQuickly》进行了解。

 

代码生成

Sculptor的代码生成过程定义为Maven构建工程的一部分,如使用命令mvn install将会执行代码生成。当进行部署时使用mvn generate-sources,将只会生成代码,而没有编译、测试和打包过程。

代码生成后被手动编写完善的代码将会视图成为完整的构件,而其他生成的构件将会每次都重新生成和覆盖。代码生成的保存分离如下图所示:


 

每次都会重新生成的代码不应加入版本控制。这里有两种类型的构件位于每个文件系统之中。sourceresources 目录结构如下图所示。这里同样展示了包名和生成的class类。这些名称都可以很容易的修改。


 

与其他代码生成工具相比,Sculptor一个很强大的亮点在于贯穿于整个完整的应用,而不仅仅是还需要很困难的进行整体设计填充的一些代码片段。

Sculptor通过简单的设计,减少了大量50%相似度一致的需要手工编写的代码。

领域模型层

Sculptor的语境中,领域模型Domain Object是一些通用的对象,如EntityValue ObjectBasic Type

实体拥有标识和状态,并且可以在其生命周期中改变。对于Value Objects 属性的值是感兴趣的,而不是其对象本身,Value Objects 一般来说都是不可变的。Basic Type用于定义基础类型,如钞票。Basic Type 是属于Value Object,并且和Domain Object仓库与同一张表中,并被Domain Object引用,它可以通过JPA embedded内嵌进行协作。

Domain Object可以被实现为原始的POJOs或者EJB3的实体。它们可以拥有相同的属性和引用其他Domain ObjectDomain Object当然可以包含行为,否则无法成为一个胖Domain Object。尽管如此,行为逻辑一般通过手工编写,而不会定义到DSL中。

这里很有可能定义一个不会被持久化于数据库的Value Object。这里的例子展示用于服务操作请求的参数和返回值。这里也很有可能被定义为一个原始的Data Transfer Object,从而运用于外部的服务,例如web service

以下是Library例子中定义在DSL文件中的一部分Domain Object

Entity Person {

String ssn key length="20"

String country key length="2"

Integer age

reference @PersonName name

}

BasicType PersonName {

String first

String last

}

ValueObject MediaCharacter {

String name not changeable

reference Set<@Person> playedBy

reference Set<@Media> existsInMedia opposite characters

}

对于Domain ObjectSculptor将生成如下内容:

ü  一部分Domain Object的数据;

ü  Domain ObjectJPAHibernate对属性和关联的注解;

ü  Data Transfer Object JAXB注解;

ü  构造函数和访问方法;

ü  Hibernate验证框架注解;

ü  hashCode toString方法;

ü  findByCondition使用的属性元数据;

ü  数据库定义脚本;

ü  可视化类图;

ü  可以点击预览的HTML格式化文档;

 

生成代码基类与需手工编写实现逻辑的代码子类分离。在图Figure 3, "Separation of generated and hand written". 在子类中增加方法,实现Domain Object的行为。这些子类只会被生成一次,之后便不会被代码生成器重写。当然,你也可以删除掉它们后,重新由代码生成器再次生成。对于Hibernate而言,equalshashCode是必须的,Sculptor很注重实现细节,这个例子展示了最佳实践。唯一需要做的就是在Domain Object属性中标识natural key,否则Sculptor将会自动生成UUIDSculptor DSL基于贯穿配置的理念,一个例子说明Entities生成是将是默认可以进行审计,这意味着这些对象被保存时,一个拦截器将会自动的更新信息记录是谁、在什么时间新增或修改了对象的属性,这些功能将会默认的增加到可审计的Domain Object中,当然你可以将此默认功能关闭,对于Value Objects默认不会有审计功能。

服务层

Services在领域模型Domain Model中扮演服务层的职责,它为客户端提供了一组定义良好可用操作。

Services 默认被注解为@ServiceSpring框架实现接口和实现类,当然也支持EJB3无状态的session bean

对于事务的绑定位于服务层,JPA/Hibernatesession划分和错误处理以SpringAOP实现

。在DSL的服务层定义一个操作看上去跟原始的Java方法一样,方法中有返回类型、参数和异常抛出定义。如:

Service LibraryService {

inject LibraryRepository

inject MediaRepository

@Library findLibraryByName(String name)

throws LibraryNotFoundException;

 

saveLibrary delegates to LibraryRepository.save;

 

findMediaByName delegates to MediaRepository.findMediaByName;

 

List<@Media> findMediaByCharacter(Long libraryId,

String characterName);

 

findPersonByName delegates to PersonService.findPersonByName;

}

 

对于ServiceSculptor将生成如下内容:

ü  服务端样板代码, 接口和实现类;

ü  访问仓库和其他服务的代理机制;

ü  JAX-WS Web Services

ü  Data Transfer Objects (DTO) with JAXB annotations

ü  消费端样板代码;

ü  错误处理和日志;

ü  Spring配置文件和XML配置文件;

ü  JUnit 测试类;

ü  可视化类图;

ü  可以点击预览的HTML格式化文档;

 

在服务实现过程中,可以向服务手动添加业务行为。你可以很轻松的实现一个仓库或其他服务的代理,而这一起只需在DSL原始文件中对业务行为操作的名字进行一下申明,返回类型和参数将会移植到代理的操作之中。

Sculptor也可以生成消息的消费,这是由EJB Message Driven Beans为纯粹的EJB3目标实现。

存储仓库层

存储仓库repositories封装了所有从数据库查询获取Domain Object的技术细节,同样也可以用于持久化新对象和删除对象。

域对象的仓库接口遍布于整个域对象,它提供了访问域对象的根源数据中心接口。

Repository MediaRepository {

int getNumberOfMovies(Long libraryId) delegates to AccessObject;

save;

findByQuery;

List<@Media> findMediaByName(Long libraryId, String name);

}

 

对于RepositoriesSculptor将生成如下内容:

ü  仓库样板代码

ü  Spring注解和XML配置文件

ü  泛型化的操作

n  FindById

n  findAll

n  findByKey

n  findByQuery

n  findByCondition

n  save

n  delete

仓库的默认实现包含了一个Access Objects访问对象的实现类。它意图实现域对象与数据访问层的分离解耦。仓库更加贴近于业务领域,而Access Objects访问对象更贴近于数据访问层。JPA/Hibernate 的描述代码位于Access Objects访问对象中,并不在仓库代码中。当然,你也可以选择跳过分离,将所有东西都在仓库代码中实现。

诸如上述特点,Sculptor运行时框架生成了很多泛型的操作,这些泛型操作对于仓库的通讯至关重要。然而,泛型操作不会自动仓库中添加,必须定义在DSL中,但这非常的简单!

 

CRUD GUI

胖领域模型是Sculptor的核心,但Sculptor仍然提供了前端到后端的实现。实现前端到后端到目的在于管理传统领域对象的新增、读取、修改和删除操作,同样在领域对象之间的关联也有良好的支持表现。这里提供了三种不同实现方式以供选择:

ü  Web Application with JSF and Spring WebFlow

ü  Rich Internet Application with GWT Smartclient

ü  Rich Client with Eclipse RCP


 

定制化Customization

这部分内容将视图扮演如何说明Sculptor能够进行定制开发。更多内容可以参考Sculptor Developer’s Guide

Sculptor并不是万能的适用于所有产品。尽快大部分情况下运用Sculptor进行大多数系统开发是一个不错的选择,但迟早还是需要定制化的开发内容。很多功能可以轻松的修改属性文件或AOP实现,而还有一部分内容的修改将需要更多的努力。尽管如此,Sculptor承认并不能解决所有问题,但是其设计框架和文档都使得开发者很容易的可以全面的控制这项工具。

对实现框架的支持

默认情况下,最终技术实现框架的主要包括如下:

ü  JPA with Hibernate as provider

ü  Spring with annotations, no EJBs

ü  Web CRUD GUI client with JSF, Facelets, and Spring Web Flow

ü  In-memory Hsqldb as database

ü  Deployment as war in Jetty

默认的实现对开发环境提供了一个良好的开始,它仅仅只依赖一些很少的外部软件,如数据库和中间件服务器。

对于这些技术领域,Sculptor还提供了一些其他可选择替代的技术支持,如:



内部设计

Sculptor 基于XtextXpand实现,并且是一个基于Apache 2 License的开源软件。


 

1.         开发者使用DSL编辑器的插件编辑应用的描述model.btdesign文件,如:实体模型的源文件进行代码生成流程。DSL的约束限制在编辑时进行校验;

2.         在生成应用描述代码的过程中,workflow.oaw将会被执行,它不会包含太多内容;

3.         它将会调用描述了代码生成流程的sculptorworkflow.oaw文件;

4.         DSL模型将会被转换成为一个被定义在sculptormetamodel.ecore原型的模型,这个原型将定义在EMF Ecore中;

5.         进行约束限制的校验;

6.         模型将会被再次转换,这是它将修改增加一些默认值;

7.         再次进行约束限制的校验;

8.         最后,真实的Java代码和配置文件生成了,它基于使用XPand语言编写的,定义在代码生成模板之中。这些模板的实际值将由XtendJava帮助类完成;

9.         原始技术的属性不属于DSL或者元数据,它将被模板和帮助类使用;

属性文件

你可以定制化修改简单的配置属性文件,如:

ü  替换整个或者部分运行时框架;

ü  新增客户化的泛型Access Objects

ü  可以很容易的更换选择拆开即用的数据库,如MySQLOraclePostgreSQL

ü  定义DSL类型与数据库类型、Java类型的映射文件;

ü  包名重命名;

ü  在仓库中使用JPA/Hibernate的支持替代Access Objects

修改代码生成模板

实际的代码生成使用XPand完成,这是一个简单而强大的模板语言。这些模板可以通过很精简的定义被很好描述结构化,例如方法。

你可以在Xpand中使用AOPAspect-Oriented Programming)修改代码生成模板,你可以覆盖原始模板文件的定义。例如如果你需要替换UUID的生成,那么:

«IMPORT sculptormetamodel

2012-01-31 16:25:47 haha_mingg 阅读数 2471
1.介绍DDD概念
    Eric Evans的“Domain-Driven Design领域驱动设计”简称 DDD,它是一套综合软件系统分析和设计的面向对象建模方法,或者可称为MDD模型驱动方法的一种,区别于MDA模型驱动架构。它是一种分析设计建模方法,它倡导统一语言,提出了实体和值对象以及聚合根等概念,借助DDD我们能够在结构理清需求中领域模型。 

  过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。

  DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。

  DDD是解决复杂大型软件的一套行之有效方式,在国外已经成为主流。DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性, 能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。领域模型的价值在于提供一种通用的语言,使得领域专家和软件技术人员联系在一起,沟通无歧义。
2.DDD的技术关键点,如下所示:

  2.1面向对象建模与数据库建模两种分析设计方法的比较
  数据库驱动设计与对象建模是决定软件不同命运的两大派别,谁可以让软件更具有生命,维护拓展更方便?伸缩性更强?

  2.2对象和关系数据库的天然阻抗
  软件是讲究方法的,要谈方法,这个世界只有两种:一是将复杂问题简单化的方法;另一是将简单问题复杂化的方法。对于软件这个领域,你只能选择前者。

  2.3 面向对象与领域建模
  据调查,目前有70%左右程序员是在使用OO语言编写传统过程化软件,缺乏完整的面向对象思维方法的教育和培训是基本根源,本文对软件开发中几个常见问题提出了独立的见解及尖锐的观点

  2.4 Evans DDD 领域建模
  如何提炼模型,而不是数据表,进而精化模型对象,使其能够反映领域概念基本本质是一个复杂过程,Evans DDD是2004年提出的具备革命性影响的软件思想。

   2.5 DDD(Evans DDD  Domain-Driven Design领域驱动设计),领域模型驱动设计(Evans DDD)之模型提炼 
领域建模是一种艺术的技术,不是数学的技术,它是用来解决复杂软件快速应付变化的解决之道。如何从职 责和协作中发现丰富对象?给出了DDD具体实践中一些具体细节,是和DDD配合一起进行面向对象分析设计的好方法。

  2.6 DCI架构是什么?
  DCI架构:DCI: 对象的Data数据, 对象使用的Context场景, 对象的Interaction交互行为

  2.7 Domain Events异步应用
  领域驱动设计和异步架构完美实战解决之道。

  2.8 DDD DCI和领域事件
  将DDD DCI Event sourcing结合在一个案例中,展示OOA和OOD实现过程,直至可运行的源代码。

  2.9 DSM:Domain-Specific Modeling
  DSM是超越UML/MDA一种新的建模方法,它成倍提高软件开发效率。

  2.10 四色原型
  我们在一个软件革命的开始,它象90年代我们看到的面向对象编程从传统过程语言中抽象出来一样。 如果说GOF设计模式开辟了OO对象设计新时代,那么原型模式和MDA将开辟后十年的软件新时代。

  2.11 Feature-Driven Development特征驱动开发
  使用JdonFramework等现代Model/Service框架是在什么项目工程背景下进行的?不是以前的XP(Extreme Programming )或RUP,而是FDD。

  2.12 UML和Java的阻抗
  如果Java和UML这种发展概念不匹配下去,我们真的要问UML过时了吗?

  2.13 状态对象:数据库的替代者
  这是一个实战中非常重要但是容易被忽视的概念,说它重要,是因为它比数据库重要;说它容易被忽视也是同样的原因,它经常被数据库概念替代。


    3.其他的一些概念

     DCI: Data数据模型, Context上下文或场景, Interactions交互行为是一种新的编程范式,由MVC发明人Trygve Reenskaug提出。
    E文: DCI : Data Context interactions in context, the interactions of Role will be assigned to data model;
     DCI的关键是:
     1. 要让核心模型非常瘦.
     2. 逻辑或行为应该放在角色这个类中。
2010-07-27 11:51:41 a1000005aa 阅读数 227
聚合(Aggregation):
这是一种松散的对象间的关系.举个例子:计算机和他的外围设备就是一例.
用来表示拥有关系或者整体与部分的关系。
组合(Composition):
这是一种非常强的对象间的关系,举个例子,树和它的树叶之间的关系.
在一个合成里,部分与整体的生命周期都是一样的。一个合成的新对象完全拥有对其组成部分的支配权。包括他们的创建和毁灭。

这两个非常的相似,
聚合:
• 聚合有时能够不依赖部分而存在,有时又不能
• 部分可以独立于聚合而存在
• 如果有一部分遗失,聚合会给人一种不完全的感觉
• 部分的所有权可以由几个聚合来共享,比如打印机
[通过接口设置]
组成:
• 部分某一时刻只能属于某一个组成
• 组成唯一的负责处理它的所有部分--这就意味着负责他们的创建与销毁
• 倘若对于部分的职责由其他对象来承担的话,组成也就可以放松这些职责。
• 如果组成销毁的话,它必须销毁所有的部分,或者把负责他们的权利转移给其他对象。
[通过代码组合]

模型驱动设计(Model-DrivenDesign)抛弃了分裂分析模型与设计的做法,使用单一的模型来满足这两方面的要求。这就是领域模型!

领域模型把分析和设计放在一起,单一的领域模型同时满足分析原型和软件设计,如果一个模型实现时不实用,重新寻找新模型。[只用单一的模型来满足分析与设计,不能满足就重新寻找模型]如何寻找模型?模型又是一个什么?
根据Eric的理论,业务层将细分为两个层次:应用层和领域层。

应用层:定义软件可以完成的工作,并且指挥具有丰富含义的领域对象来解决问题,保持精练;不包括业务规则或知识,无业务情况的状态;Action[这个代码更改的概率为0]

领域层:负责表示业务概念、业务状态的信息和业务规则,是业务软件核心。层次之间必须清晰分离,每个层都是内聚的,并且只依赖它的下层.[在领域层会建立模型,比如用户模型,保存了用户的所有属性,用户所有的功能,全部面向接口,依赖下层实现,也就是以抽象的方式建立模型,就也就是在分析和设计的时候设置的用户模型,比如以下类图]


分析与设计[设置顶级接口很重要]
用户顶级接口 User

UserInfo getUserInfo() --得到用户信息:用户属性
void Speak() --说话:用户功能
void Walk() --走路:用户功能


看到这里会不会觉得用户接口很简单,很简洁.[爆露给外部调用的方法,在设计的时候已经确定]

用户接口的功能内聚,用户模型保存用户属性,所有功能面向接口,依赖下层实现.
接口抽象类 AbstratorUser

UserInfo userInfo; --用户属性

//用户操作对象.
Abstrator UserOperator getUserOperator() –用户操作对象信息,依赖下层

//overwriter
Speak(){
getUserOperator ().Speak();
}

//overwriter
Walk(){
getUserOperator ().Walk();
}
//用户信息在初始的时候留给子类设置
Void setUserInfo(UserInfo userInfo){
This.userInfo=userInfo
}

//overwriter
UserInfo getUserInfo(){
Return userInfo;
}

[对业务逻辑的初步封装,蓝色部分是重写方法,而红色部分是需要依赖下层建筑的]

以上为设计阶段完成的,而下层建筑就交给开发工程师来完成,不同的开发工程师的水平和风格可能不一样,但是完全分离的方式使各模块[用户信息,用户操作]都能够正常运行,耦合为0. [这个代码更改的概率为0]

现在Action层代码更改为0,接口层和抽象层代码修改为0,那么当用户逻辑变的时候,只需要修改下层建筑就可以了!.

UserInfo为用户信息,这是一个实体类,其各对象的属性采用组合的方式来连接
Address,Account,Family.如果需要添加一些Friend等信息很方便,与其他模块不会有耦合.
UserOperator为用户操作对象,这是一个接口,依赖下层建筑.如果用户Speak的方式从中文变成英文了,只需要修改下层建筑就可以了.

这就是DDD,领域驱动开发,只依赖下层建筑,而下层建筑就是最简单的增删改查操作.也就是业务最需要变动的地方,领域驱动可以把一个项目分成一个域,而这个域能够包含这个项目的所有信息,其实DDD并不算新的技术,他只是提供了一个概率,分析与设计的组合可以把业务逻辑理清,减少上层建筑的缺陷,而下层建筑是可以随机换的,上层建筑一般是基本接口和抽象,只要业务逻辑没有分析出错,就不需要改动.而DDD把层分成两层,应用层和领域层,也是从整体考虑,应用层调用领域层的接口,其实领域层也可以分层,他只是让系统的策划更简单,不用考虑那么多层,系统都是从简单到复杂,这相当于数据挖掘技术,一张图很简单,再挖一下也很简单,再挖一下也很简单,应用不同的项目,不同的模块,基于接口和抽象的设计,并能从全局考虑就是DDD的思想.

现在来回答领域模型是什么:
领域模型是对领域内的概念类或现实世界中对象的可视化表示。
现实中的人 User
人的属性 getUserInfo()
人说话 Opertator.Speak()
人走路 Operator.Walk()
如何寻找模型:
在业务专家和设计专家一些交流的时候,设计专家能够从业务专家的描述中抽取出实体及实体间的关系[泛化、依赖和关联,关联又分了一般关联、聚合、组合等等]
2018-06-17 20:47:18 FS1360472174 阅读数 2082

摘要

习惯了MVC模式,习惯了敏捷开发,习惯了了小步快跑,还适合谈论领域驱动开发吗。领域开发是否就是慢节奏的开发,
本文结合自己的开发经历,和大家聊聊这个话题。

#一.业务代码是如何写烂的#
java web开发通常都是mvc模式,从早期的ssh逐渐到Spring+ Mybatis。所以通常一个工程的项目结构图就是

  • controller
  • service
  • manager
  • dao

问题1: bean的职责不清

对应的bean就是
PO/DO(Persistence Object/Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象
DTO(Data Transfer Object):数据传输对象,Service 和 Manager 向外传输的对象。
BO(Business Object): 业务对象。可以由 Service 层输出的封装业务逻辑的对象。一般不需要,
Query:数据查询对象,各层接收上层的查询请求。
VO(View Object): contoller层对外提供的

一般至少有PO/DTO/VO 三层结构,但是对于有些项目结构,业务比较简单,就是CRUD,有些开发人员就只有两层,controller到dao层就完了,然后bean的定义也特别混乱,随意创建,导致bean漫天飞,通常除了自己明白其中的含义,其他人都不明白,复用性极差。

问题2:面向过程的设计
此外 bean中都是属性,除了equals方法就都没有了。虽然有接口和实现,但是按照这样一套写出来的代码基本上和面向过程写的代码没有什么区别。这种开发方式bean类只有属性,没有行为。这样就会导致某一个实体的变更会散落在各个service中,而不是这个领域实体中。

问题3:不考虑业务模型
现在都是敏捷开发,导致开发人员也变得浮躁了,不分析或者草率分析需求,拿到就是干,随着业务迭代,开发人员增加,每个人各写一套,关于一个名词的定义都能有好几套写法,sql查询可能会分散到好多repo中,相同的sql可能会在不同的地方写上好几遍。关键是发现之前的模型定义错了,数据库的ER图设计有问题,仍然不会去更改,因为总是有新的需求会来,然后拼了命的做需求,留下一堆烂代码无法维护,最后连自己都不想看。

二. 领域模型是如何发挥作用的

比如说一个平台,一开始只有一种用户身份,后来平台做大了,开始做交易了,区分出了商家了,和买家了。产品提了个需求开发一个商家入驻流程,吭哧吭哧开发完了。一段时间后,开始需要有中间推销商了,一部分买家可以变成中间推销商了,那这时候产品跟你说,不用搞入驻流程,直接赋予原来的买家额外属性,来区分是否可以推销商品。这时候你能同意吗?

当然不行了,因为这个业务需求本质是就是交易,有买家,有卖家,有中间商。他们属于交易维度的不同实体,是同一个层次的,而用户则是不同的层次。一开始产品只会有需求说判断是中间商就可以,没有其他的了。等你在用户实体上加了一堆属性后,过了一段时间后,产品就会来跟你说,不好意思,哥们,卖家想管理中间商,需要中间商提供一些资质,你再帮我加几个属性。然后你的用户实体的模型开始无限扩张的模式了。对于产品来说,他是无所谓的,快速上线验证,验证了不行,换另外一条路,但是作为开发就被坑的天天加班了。

所以领域模型可以帮你解决,通常一些对于一些通用的领域,你可能很好找到对应模型设计,比如说订单,商品,抽奖,优惠券。但是这也需要你去阅读相关的产品文档,领域设计,当然如果你有一个靠谱的产品,你可能会轻松很多。而有一些不是通用领域的,所以需要你与你行业领域专家去深入聊,才能设计的出来领域模型,另外一点就是通常在一开始,会有些领域模型设计的有问题,需要不断的思考,纠正。

这里写图片描述

没有更多推荐了,返回首页