精华内容
下载资源
问答
  • DDD领域驱动模型设计

    万次阅读 2015-11-11 17:37:41
    背景使用DDD开发大概也有五个月的时间了,由于当时公司导师的推荐,第一次接触DDD领域驱动到现在彻底迷恋这种开发的模式,为其思想的奥妙所折服,一直以来,总想花一点时间来总结一下,正直光棍节(天猫狂欢购物节)...

    背景

    使用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的开发人员都可以很好的维护。

    展开全文
  • 然而,在领域驱动设计中,层次和包的划分看起来与我们的结构又有一定区别,本文主要讨论DDD中的分层架构及每层的意义,以及与传统的三层架构的区别。 1. 为什么要分层 软件设计中分层的设计随处可见,但是分层能带来...
        

    在分解复杂的软件系统时,分层是我们最常用的手段之一。然而,在领域驱动设计中,层次和包的划分看起来与我们的结构又有一定区别,本文主要讨论DDD中的分层架构及每层的意义,以及与传统的三层架构的区别。

    1. 为什么要分层

    软件设计中分层的设计随处可见,但是分层能带来什么好处呢?或者说,我们为什么要考虑分层架构呢?

    由于现实世界的复杂性,分层可以提供一个相对高层的视角来分解和简化我们的问题,此外分层也可带来可测试、可维护性、灵活性、可扩展性等方面的好处。

    • 简化复杂性,关注点分离,结构清晰;
    • 降低耦合度,隔离层次,降低依赖(上层无需关注下层具体实现),利于分工、测试和维护(可维护性);
    • 提高灵活性,可以灵活替换某层的实现;
    • 提高扩展性,方便实现分布式部署;

    看起来十分简单,好像就是把系统划分为一定的层数,并把他们堆叠组织起来。但是,当落实到具体的实践时,如何划分、各层存在的意义、如何取舍以及相应的依赖关系却并没有想象中那么容易,边界的重合部分、不同场景下关注点、层次内部的具体分解以及层次的粒度等都是我们需要考虑的问题。

    2. 什么是分层架构

    2.1 分层的历史

    最广为人知的应该就是经典的三层架构:展示层、业务逻辑层、数据访问层。

    Martin Fowler在《企业应用架构模式》中也是类似的三层进行展开的:表现层,领域层,数据源层。

    还有各种其他分层架构,这里就不一一描述了。

    面对如此多的分层架构,我们不禁思考,他们分层的依据又是什么?能否抽象出一些相同点和不同点?又该在什么时候加入哪些合适的中间层?在实践中我们又该采取怎样的架构呢?

    2.2 分层的本质

    分层其实是把一系列相同或相似的对象进行分类放在同一层,然后根据他们之间的依赖关系再确定上下层次关系。可以看出,分层的核心在于分类和关联。

    通常,我们可以将系统划分为变化较大的业务部分和相对稳定的技术部分;对于业务来说,又可划分为展示部分(前台)和内部处理逻辑(后台)两大部分;展示又可分为数据/页面部分和接口部分。如此不断的进行细分和抽象,我们可以迭代出更细粒度的分类/层次,如下所示:

    业务:需要重点关注,我们的目的也是分离出具体的业务领域逻辑:

    • 外部展示(表现层/接口层):数据、页面(web)、远程接口(interface/api)
    • 内部逻辑处理:应用逻辑(应用层/服务层)、具体业务逻辑(领域层)

    技术:相对稳定,具体业务无关(基础设施层)

    • 数据访问(数据访问层)
    • 日志、安全、异常、缓存等

    当然,分类并不唯一,基于不同的视角我们可能会有不同的分类标准。比如数据访问层也可以归类到业务相关/内部逻辑处理的部分,因为可能涉及到一些对具体业务表的操作。

    此外,根据问题领域和解决方案的复杂程度,我们可以有不同的层次。比如在业务不太复杂时,我们可以把应用层和领域层合并为一层。

    3. DDD经典分层架构

    上面我们在分析分层的本质时也提到了一些基本的层次和分类标准,但那只是一个非常粗粒度的划分。

    在实际决策时,我们需要知道各层的职责、意义以及相应的场景;而落实到代码层面时,我们还需要知道各层所包含的具体内容、各层的一些常见的具体策略/模式、层次之间的交互/依赖关系。

    首先我们来看一下Evans在《领域驱动设计》中提到的分层架构。

    1233356-dcfd1e89a7819449.jpg
    image

    问:为什么要分成这样的四层?

    分层主要目的是为了简化复杂性,系统中最复杂的部分应该就是我们的业务逻辑。当系统交互或者工作流比较复杂时,我们会考虑从业务逻辑中抽出这部分作为应用层。而各个领域内的代码则化为领域层,这样层级结构更加清晰。

    3.1 用户界面层/表示层

    用户界面层负责向用户显示信息和解释用户指令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。

    该层包含与其他应用系统(如web服务、RMI接口或web应用程序以及批处理前端)交互的接口与通信设施。

    它负责输入参数的解释、验证以及转换。另外,它也负责输出参数的序列化,如通过HTTP协议向web浏览器或web服务客户端传输HTML或XML,或远程Java客户端的DTO类和远程外观接口的序列化。

    可以看出,该层的主要职责是与外部用户(包括web服务、其他系统)交互,如接受用户的反馈,展示必要的数据信息。主要包含web部分和远程服务部分等。

    web部分一般包含常见的Servlet,Controller等组件,而远程接口部分主要由Facade、DTO和Assembler等构成。

    • Facade:远程外观,一个粗粒度的外观,不含任何领域逻辑
    • DTO:数据传输对象
    • Assembler:对象组装器,负责数据传输对象与领域对象相互转换,不对外暴露

    问:参数的校验为什么在用户界面层?领域层的校验和用户界面层的校验有什么不同?

    校验应该取决于校验的内容,一般推荐尽早校验,不过这里主要是进行一些简单的、不涉及业务规则的校验。具体的业务规则的校验放在领域层。

    问:为什么需要DTO?DTO和VO是同一个东西吗?

    领域对象关系比较复杂,很难序列化,而且用户很多时候并不需要整个模型,大部分时候需要的只是其中的一部分内容,DTO可以有效减少网络调用的开销。此外,领域模型内部的逻辑也无需暴露给外部。

    DTO一般用于远程服务,如果是内部使用的话,一般可以直接使用领域对象。

    VO中有前端状态信息,比如成功失败等。

    问:为什么需要Assembler?

    主要目的是解耦,负责数据传输对象和领域对象之间的相互转换。BeanUtils也可以做到相应的功能(dozer相对好一些),不过Assembler更为清晰,安全与可控,缺点在于手工代码量稍多。

    3.2 应用层

    应用层定义了软件要完成的任务,并且指挥表达领域概念的对象来解决问题。该层所负责的工作对业务来说意义重大,也是与其他系统的应用层进行交互的必要通道。

    应用层要尽量简单。它不包含任务业务规则或知识,只是为了下一层的领域对象协助任务、委托工作。它没有反映业务情况的状态,但它可以具有反映用户或程序的某个任务的进展状态。

    应用层主要负责组织整个应用的流程,是面向用例设计的。该层非常适合处理事务,日志和安全等。相对于领域层,应用层应该是很薄的一层。它只是协调领域层对象执行实际的工作。

    综上所述,应用层是表达user case和user story的主要手段,主要用于协调领域模型与其他应用组件的工作(并不处理业务逻辑)。

    应用层中主要组件是Service,因为主要职责是协调各组件工作,所以通常会与多个组件交互,如其他Service,领域对象,Repostitory等。

    一种比较常见的做法是:应用层通常接受来自用户界面层的参数,再通过Repostitory获取到聚合示例,然后执行相应的命令操作(很薄的一层)。

    问:为什么要有应用层?

    业务比较复杂时,我们会从业务逻辑中拆分出应用层和领域层。

    如果在领域对象中事先针对具体应用的逻辑,会降低应用之间的可重用性。

    此外,如果将来需要加工作流之类的工具来实现应用逻辑,如果之前是混杂在一起的话则不好拆分。

    3.3 领域层/模型层

    领域层主要负责表达业务概念,业务状态信息和业务规则。

    Domain层是整个系统的核心层,几乎全部的业务逻辑会在该层实现。

    领域模型层主要包含以下的内容:

    • 实体(Entities):具有唯一标识的对象
    • 值对象(Value Objects): 无需唯一标识
    • 领域服务(Domain Services): 一些行为无法归类到实体对象或值对象上,本质是一些操作,而非事物
    • 聚合/聚合根(Aggregates & Aggregate Roots): 聚合是指一组具有内聚关系的相关对象的集合,每个聚合都有一个rootboundary
    • 工厂(Factories): 创建复杂对象,隐藏创建细节
    • 仓储(Repository): 提供查找和持久化对象的方法

    关于各个元素的具体含义、职责以及相关误区,可参考领域建模核心概念解析.
    对于这些具体的对象,可定义一些标准领的Annotation来规范。

    3.4 基础设施层

    基础设施层为上面各层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件。

    基础设施层以不同的方式支持所有三个层,促进层之间的通信。
    基础设施包括独立于我们的应用程序存在的一切:外部库,数据库引擎,应用程序服务器,消息后端等。

    作为基础设施层,InfrastructureInterfacesApplicationDomain三层提供支撑。所有与具体平台、框架相关的实现会在Infrastructure中提供,避免三层特别是Domain层掺杂进这些实现,从而“污染”领域模型。Infrastructure中最常见的一类设施是对象持久化的具体实现。

    问: Repository作用是什么?和DAO的关系
    之前对Repository也曾有过误解(在我们的系统中有一个repository层位于daoservice之间)。
    DAO主要是从数据库表的角度来看待问题的,并且提供CRUD操作(只是对数据库表的一个封装),是一种面向数据处理的风格(事务脚本);
    Repository(资源库)和Data Mapper(数据映射器)更加面向对象,通常用于领域模型中。
    因为数据访问层的暴露可能会破坏对象的封装性,对象的关系和数据一致性也难以维护,所以 应该尽量避免在领域模型中使用DAO模式,推荐使用聚合本身来管理业务逻辑。

    4. 模型的形态

    不同的架构、不同的层、不同的应用场景中有着不一样的建模需求,因此表达相同概念的模型可能会有不同的形态,例如:

    • 充血模型:领域模型架构中包含了领域逻辑和领域属性的领域模型。
    • 失血模型:传统三层架构中只有get/set方法,没有业务逻辑的POJO对象。
    • 贫血模型:类似充血模型,但是不包括持久化相关逻辑。
    • PO(Persistant Object):持久化对象,即DAO从JDBC取出来的对象。传统三层架构中,PO即POJO组件中的对象,存在于DAO和Service之间。
    • DO(Domain Object):领域对象,领域模型架构中,PO从数据库取出来后,有一个“重建”的概念,即根据数据还原实体,这个被还原的实体就是DO,存在于DAO和Service之间。
    • DTO(Data Transfer Object):数据传输对象。对传统三层架构来说,该对象存在于Service和Controller之间。PO到DTO的转换可以在Service或Controller中实现。
    • VO(View Object):视图对象。Controller在返回DTO给视图时,可能还需要包括状态信息例如操作成功/失败的状态码、提示文本等。这时就需要在DTO外面再包一层,即View Object。该对象存在于Controller和Web之间,由Controller进行装配

    参考文档:
    https://my.oschina.net/hosee/blog/919426

    展开全文
  • 然而,在领域驱动设计中,层次和包的划分看起来与我们的结构又有一定区别,本文主要讨论DDD中的分层架构及每层的意义,以及与传统的三层架构的区别。 1. 为什么要分层 软件设计中分层的设计随处可见,但是分层能...

    在分解复杂的软件系统时,分层是我们最常用的手段之一。然而,在领域驱动设计中,层次和包的划分看起来与我们的结构又有一定区别,本文主要讨论DDD中的分层架构及每层的意义,以及与传统的三层架构的区别。

    1. 为什么要分层

    软件设计中分层的设计随处可见,但是分层能带来什么好处呢?或者说,我们为什么要考虑分层架构呢?

    由于现实世界的复杂性,分层可以提供一个相对高层的视角来分解和简化我们的问题,此外分层也可带来可测试、可维护性、灵活性、可扩展性等方面的好处。

    • 简化复杂性,关注点分离,结构清晰;
    • 降低耦合度,隔离层次,降低依赖(上层无需关注下层具体实现),利于分工、测试和维护(可维护性);
    • 提高灵活性,可以灵活替换某层的实现;
    • 提高扩展性,方便实现分布式部署;

    看起来十分简单,好像就是把系统划分为一定的层数,并把他们堆叠组织起来。但是,当落实到具体的实践时,如何划分、各层存在的意义、如何取舍以及相应的依赖关系却并没有想象中那么容易,边界的重合部分、不同场景下关注点、层次内部的具体分解以及层次的粒度等都是我们需要考虑的问题。

    2. 什么是分层架构

    2.1 分层的历史

    最广为人知的应该就是经典的三层架构:展示层、业务逻辑层、数据访问层。

    Martin Fowler在《企业应用架构模式》中也是类似的三层进行展开的:表现层,领域层,数据源层。

    还有各种其他分层架构,这里就不一一描述了。

    面对如此多的分层架构,我们不禁思考,他们分层的依据又是什么?能否抽象出一些相同点和不同点?又该在什么时候加入哪些合适的中间层?在实践中我们又该采取怎样的架构呢?

    2.2 分层的本质

    分层其实是把一系列相同或相似的对象进行分类放在同一层,然后根据他们之间的依赖关系再确定上下层次关系。可以看出,分层的核心在于分类和关联。

    通常,我们可以将系统划分为变化较大的业务部分和相对稳定的技术部分;对于业务来说,又可划分为展示部分(前台)和内部处理逻辑(后台)两大部分;展示又可分为数据/页面部分和接口部分。如此不断的进行细分和抽象,我们可以迭代出更细粒度的分类/层次,如下所示:

    业务:需要重点关注,我们的目的也是分离出具体的业务领域逻辑:

    • 外部展示(表现层/接口层):数据、页面(web)、远程接口(interface/api)
    • 内部逻辑处理:应用逻辑(应用层/服务层)、具体业务逻辑(领域层)

    技术:相对稳定,具体业务无关(基础设施层)

    • 数据访问(数据访问层)
    • 日志、安全、异常、缓存等

    当然,分类并不唯一,基于不同的视角我们可能会有不同的分类标准。比如数据访问层也可以归类到业务相关/内部逻辑处理的部分,因为可能涉及到一些对具体业务表的操作。

    此外,根据问题领域和解决方案的复杂程度,我们可以有不同的层次。比如在业务不太复杂时,我们可以把应用层和领域层合并为一层。

    3. DDD经典分层架构

    上面我们在分析分层的本质时也提到了一些基本的层次和分类标准,但那只是一个非常粗粒度的划分。

    在实际决策时,我们需要知道各层的职责、意义以及相应的场景;而落实到代码层面时,我们还需要知道各层所包含的具体内容、各层的一些常见的具体策略/模式、层次之间的交互/依赖关系。

    首先我们来看一下Evans在《领域驱动设计》中提到的分层架构。

     

    问:为什么要分成这样的四层?

    分层主要目的是为了简化复杂性,系统中最复杂的部分应该就是我们的业务逻辑。当系统交互或者工作流比较复杂时,我们会考虑从业务逻辑中抽出这部分作为应用层。而各个领域内的代码则化为领域层,这样层级结构更加清晰。

    3.1 用户界面层/表示层

    用户界面层负责向用户显示信息和解释用户指令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。

    该层包含与其他应用系统(如web服务、RMI接口或web应用程序以及批处理前端)交互的接口与通信设施。

    它负责输入参数的解释、验证以及转换。另外,它也负责输出参数的序列化,如通过HTTP协议向web浏览器或web服务客户端传输HTML或XML,或远程Java客户端的DTO类和远程外观接口的序列化。

    可以看出,该层的主要职责是与外部用户(包括web服务、其他系统)交互,如接受用户的反馈,展示必要的数据信息。主要包含web部分和远程服务部分等。

    web部分一般包含常见的Servlet,Controller等组件,而远程接口部分主要由Facade、DTO和Assembler等构成。

    • Facade:远程外观,一个粗粒度的外观,不含任何领域逻辑
    • DTO:数据传输对象
    • Assembler:对象组装器,负责数据传输对象与领域对象相互转换,不对外暴露

    问:参数的校验为什么在用户界面层?领域层的校验和用户界面层的校验有什么不同?

    校验应该取决于校验的内容,一般推荐尽早校验,不过这里主要是进行一些简单的、不涉及业务规则的校验。具体的业务规则的校验放在领域层。

    问:为什么需要DTO?DTO和VO是同一个东西吗?

    领域对象关系比较复杂,很难序列化,而且用户很多时候并不需要整个模型,大部分时候需要的只是其中的一部分内容,DTO可以有效减少网络调用的开销。此外,领域模型内部的逻辑也无需暴露给外部。

    DTO一般用于远程服务,如果是内部使用的话,一般可以直接使用领域对象。

    VO中有前端状态信息,比如成功失败等。

    问:为什么需要Assembler?

    主要目的是解耦,负责数据传输对象和领域对象之间的相互转换。BeanUtils也可以做到相应的功能(dozer相对好一些),不过Assembler更为清晰,安全与可控,缺点在于手工代码量稍多。

    3.2 应用层

    应用层定义了软件要完成的任务,并且指挥表达领域概念的对象来解决问题。该层所负责的工作对业务来说意义重大,也是与其他系统的应用层进行交互的必要通道。

    应用层要尽量简单。它不包含任务业务规则或知识,只是为了下一层的领域对象协助任务、委托工作。它没有反映业务情况的状态,但它可以具有反映用户或程序的某个任务的进展状态。

    应用层主要负责组织整个应用的流程,是面向用例设计的。该层非常适合处理事务,日志和安全等。相对于领域层,应用层应该是很薄的一层。它只是协调领域层对象执行实际的工作。

    综上所述,应用层是表达user case和user story的主要手段,主要用于协调领域模型与其他应用组件的工作(并不处理业务逻辑)。

    应用层中主要组件是Service,因为主要职责是协调各组件工作,所以通常会与多个组件交互,如其他Service,领域对象,Repostitory等。

    一种比较常见的做法是:应用层通常接受来自用户界面层的参数,再通过Repostitory获取到聚合示例,然后执行相应的命令操作(很薄的一层)。

    问:为什么要有应用层?

    业务比较复杂时,我们会从业务逻辑中拆分出应用层和领域层。

    如果在领域对象中事先针对具体应用的逻辑,会降低应用之间的可重用性。

    此外,如果将来需要加工作流之类的工具来实现应用逻辑,如果之前是混杂在一起的话则不好拆分。

    3.3 领域层/模型层

    领域层主要负责表达业务概念,业务状态信息和业务规则。

    Domain层是整个系统的核心层,几乎全部的业务逻辑会在该层实现。

    领域模型层主要包含以下的内容:

    • 实体(Entities):具有唯一标识的对象
    • 值对象(Value Objects): 无需唯一标识
    • 领域服务(Domain Services): 一些行为无法归类到实体对象或值对象上,本质是一些操作,而非事物
    • 聚合/聚合根(Aggregates & Aggregate Roots): 聚合是指一组具有内聚关系的相关对象的集合,每个聚合都有一个rootboundary
    • 工厂(Factories): 创建复杂对象,隐藏创建细节
    • 仓储(Repository): 提供查找和持久化对象的方法

    关于各个元素的具体含义、职责以及相关误区,可参考领域建模核心概念解析. 对于这些具体的对象,可定义一些标准领的Annotation来规范。

    3.4 基础设施层

    基础设施层为上面各层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件。

    基础设施层以不同的方式支持所有三个层,促进层之间的通信。 基础设施包括独立于我们的应用程序存在的一切:外部库,数据库引擎,应用程序服务器,消息后端等。

    作为基础设施层,InfrastructureInterfacesApplicationDomain三层提供支撑。所有与具体平台、框架相关的实现会在Infrastructure中提供,避免三层特别是Domain层掺杂进这些实现,从而“污染”领域模型。Infrastructure中最常见的一类设施是对象持久化的具体实现。

    问: Repository作用是什么?和DAO的关系 之前对Repository也曾有过误解(在我们的系统中有一个repository层位于daoservice之间)。 DAO主要是从数据库表的角度来看待问题的,并且提供CRUD操作(只是对数据库表的一个封装),是一种面向数据处理的风格(事务脚本); 而Repository(资源库)和Data Mapper(数据映射器)更加面向对象,通常用于领域模型中。 因为数据访问层的暴露可能会破坏对象的封装性,对象的关系和数据一致性也难以维护,所以 应该尽量避免在领域模型中使用DAO模式,推荐使用聚合本身来管理业务逻辑。

    4. 模型的形态

    不同的架构、不同的层、不同的应用场景中有着不一样的建模需求,因此表达相同概念的模型可能会有不同的形态,例如:

    • 充血模型:领域模型架构中包含了领域逻辑和领域属性的领域模型。
    • 失血模型:传统三层架构中只有get/set方法,没有业务逻辑的POJO对象。
    • 贫血模型:类似充血模型,但是不包括持久化相关逻辑。
    • PO(Persistant Object):持久化对象,即DAO从JDBC取出来的对象。传统三层架构中,PO即POJO组件中的对象,存在于DAO和Service之间。
    • DO(Domain Object):领域对象,领域模型架构中,PO从数据库取出来后,有一个“重建”的概念,即根据数据还原实体,这个被还原的实体就是DO,存在于DAO和Service之间。
    • DTO(Data Transfer Object):数据传输对象。对传统三层架构来说,该对象存在于Service和Controller之间。PO到DTO的转换可以在Service或Controller中实现。
    • VO(View Object):视图对象。Controller在返回DTO给视图时,可能还需要包括状态信息例如操作成功/失败的状态码、提示文本等。这时就需要在DTO外面再包一层,即View Object。该对象存在于Controller和Web之间,由Controller进行装配
    展开全文
  • DDD领域驱动设计

    2021-06-04 16:32:17
    DDD领域驱动设计1. 领域驱动设计1.1 什么是领域驱动设计1.2 为什么用领域驱动设计2. DDD核心知识体系2.1 DDD核心概念2.2 DDD战略战术设计2.2.1 DDD战略设计2.2.1 DDD战术设计3. DDD微服务架构模型3.1 基本架构3.1.1 ...

    1. 领域驱动设计

    1.1 什么是领域驱动设计

    领域驱动设计(Domain Driven Design) 是一种从系统分析到软件建模的一套方法论。以领域为核心驱动力的设计体系。
    

    1.2 为什么用领域驱动设计

    面向对象设计,数据行为绑定,告别贫血模型
    优先考虑领域模型,而不是切割数据和行为
    准确传达业务规则
    代码即设计
    它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现业务和技术统一的架构演进
    
    领域驱动设计,又称"软件核心复杂性应对之道"。是一套基于对象思维的业务建模设计思想,相对于 CRUD 系统有更高的灵活性,是业务人员处理复杂问题的有效手段。
    

    2. DDD核心知识体系

    2.1 DDD核心概念

    DDD的核心知识体系主要包括领域、子域、核心域、支撑域、通用域、限界上下文、实体、值对象、聚合、聚合根等概念。 
    

    在这里插入图片描述

    2.2 DDD战略战术设计

    DDD有战略设计和战术设计之分。战略设计主要从高层"俯视"我们的软件系统,帮助我们精准地划分领域以及处理各个领域之间的关系;而战术设计则从技术实现的层面教会我们如何具体地实施DDD。
    
    战略建模-Strategic Modeling
    限界上下文(Bounded Context)
    上下文映射图(Context Mapping)
    战术建模-Tactical Modeling:
    聚合-Aggregate
    实体-Entity
    值对象-Value Objects
    资源库-Repository
    领域服务-Domain Services
    领域事件-Domain Events
    模块-Modules 
    

    在这里插入图片描述在这里插入图片描述

    2.2.1 DDD战略设计

    DDD之战略设计
    需要指出的是,DDD绝非一套单纯的技术工具集,但是我所看到的很多程序员却的确是这么认为的,并且也是怀揣着这样的想法来使用DDD的。过于拘泥于技术上的实现将导致DDD-Lite。简单来讲,DDD-Lite将导致劣质的领域对象,因为我们忽略了DDD战略建模所带来的好处。
    DDD的战略设计主要包括领域/子域、通用语言、限界上下文和架构风格等概念。
    

    领域与子域
    在这里插入图片描述限界上下文
    在这里插入图片描述领域场景分析
    在这里插入图片描述四色建模法
    在这里插入图片描述

    2.2.1 DDD战术设计

    领域驱动设计,整体包括战略和战术两部分,其中战略部分的落地需要团队合作、开发过程、流程制度等一系列支持,实施阻力相对较大。相反,战术部分,是一组面向业务的设计模式,是基于技术的一种思维方式,相对开发人员来说比较接地气,是提升个人格局比较好的切入点。
    战略设计为我们提供一种高层视野来审视我们的软件系统,而战术设计则将战略设计进行具体化和细节化,它主要关注的是技术层面的实施,也是对我们程序员来得最实在的地方。
    战术模式包含若干构造块模式,以便能够构建有效的领域模型。
    战术模式严重依赖于领域模型和通用语言,通过技术模式将领域模型和通用语言中的概念映射到代码实现中。随着模型的进化,代码实现也会进行重构,以更好的体现模型概念。当然,从技术重构角度也会发现一些隐含领域知识(概念),这些新的发现也会对领域模型产生影响。
    战术模式和通用语言一样,都工作在特定限界上下文内,其应用边界受限界上下文的保护。
    
    战术模式的作用是管理复杂性并确保领域模型中行为的清晰明确。可以使用这些模式来捕获和传递领域中的概念、关系、规则。
    每个构造块模式都具有单一职责:
    
        代表领域中的概念。如实体、值对象、领域服务、领域事件、模块等;
        用于管理对象的生命周期。如聚合、工厂、仓库等;
        用于集成或跟踪。领域事件、事件溯源等。
    

    在这里插入图片描述概念说明

    领域建模模式
    
    他们表述实现与模型间的关系,将分析模型绑定到代码实现模型。主要用于在代码中表述模型元素的模式。
    实体
    
    实体表述的是领域中的概念,它是由身份而不是属性来定义的。
    实体的身份标识在生命周期中保持不变,但其属性会发生变化。实体以身份标识作为唯一凭证,沿着时间轴,记录了实体所有变更事件。
    实体的一个实例是产品,一旦产品被生成好,其唯一身份就不会发生变化,但是其描述信息、价格等可以被多次修改。
    值对象
    
    值对象代表仅通过数据区分的领域元素和概念。用作模型中元素的描述,它不具有唯一标识。
    值对象不需要唯一标识,是因为它总是与另一个对象相关联,是在一个特定上下文中被解析的。通常,其生命周期会依附于它的关联对象(在这里,主要是实体对象)。
    值对象会当做不变对象来设计,在完成创建后,其状态就不能改变了。
    值对象比较好的例子就是现金,你无需关系货币的身份,只关心它的价值。如果有人用一张五美元钞票交换你的五张一美元钞票,也不会改变五美元本身。
    领域服务
    
    在模型中,领域服务封装了不能自然建模为值对象和实体的逻辑、流程和概念。
    它本身不具有身份和状态。它的职责是使用实体和值对象编排业务逻辑。
    领域服务的一个好例子是运输成本计算器,只要给出一组拖运货物和重量,它就能计算运输成本。
    模块
    
    模块主要用于组织和封装相关概念(实体、值对象、领域服务、领域事件等),这样可以简化对较大模型的理解。
    应用模块可以在领域模型中促成低耦合和搞内聚的设计。
    模块作用于单个领域,用于分解模型规模。子域用于限定领域模型适用范围(有界上下文)。
    对象生命周期模式
    
    相对来说,之前提到的模式重点在于表达领域概念。而对象生命周期模式,有点侧重于技术,用于表示领域对象的创建和持久化。
    聚合
    
    实体和值对象会相互协作,形成复杂的关联关系。我们需要在满足不变条件的前提下,将其拆分为一个个概念上的整体。
    通常,面对复杂的对象关系,在执行领域对象操作时,难以保证一致性和并发性。
    领域驱动设计由聚合模式来确保操作的一致性和事务的并发边界。大模型会通过不变性条件来划分,并组成概念化整体的实体和对象组,这个概念化整体便是聚合。
    聚合根之间的关系应该通过保持对另一个聚合根 ID 的引用,而非对对象本身的引用来实现。这一原则有助于保持聚合之间的边界并避免加载不必要的对象。
    不变性,是在领域模型中强制实现一致性的规则。无论何时对实体或聚合进行变更都要应用该业务规则。
    聚合外部的对象只能引用另一个聚合的聚合根,聚合中对象的任何变更都需要通过聚合根来完成。聚合根封装聚合数据并公开行为以对其进行修改。
    工厂
    
    如果实体或值对象的创建过程非常复杂,可以将其委托给工厂。工厂会确保在领域对象使用之前就满足所有的不变条件。
    如果领域对象很简单并且不具有特殊的不变条件,可以使用构造函数代替工厂。当从持久化存储中重建领域对象时,也可以使用工厂。
    仓库
    
    仓库主要用于持久化一个聚合。将聚合作为原子单元进行处理,因此,仓库操作的最小单元就是聚合,每个聚合会对应一个仓库。
    
    仓库是用来检索和存储聚合的机制,是对基础框架的一种抽象。
    其他模式
    领域事件
    
    领域事件表示问题空间中发生了一些业务人员关心的事情。主要用于表示领域概念。
    使用领域事件主要有以下两种场景:
    
        记录模型的变更历史;
        作为跨聚合通信方式。
    
    事件溯源
    
    传统的仅快照式持久化的一个替代项便是事件溯源。作为实体状态存储的替代,可以存储引发该状态的系列事件。
    存储所有的事件会提高业务的分析能力,不仅可以得知实体当前状态,还可以得知过去任意时点的状态。
    
    

    概念总结

        实体
            由唯一标识符定义
            标识符在整个生命周期保存不变
            基于标识符进行相等性检查
            通过方法对属性进行更新
            
        值对象
            描述问题域中的概念和特征
            不具备身份
            不变对象
            
        领域服务
            处理无法放置在实体或值对象中的领域逻辑
            无唯一标识
            无状态服务
            
        模块
            分解、组织和提高领域模型的可读性
            命名空间,降低耦合,提供模型高内聚性
            定义领域对象组间的边界
            封装比较独立的概念,是比聚合、实体等更高层次的抽象
            
        聚合
            将大对象图分解成小的领域对象群,降低技术实现的复杂性
            表示领域概念,不仅仅是领域对象集合
            确定领域一致性边界,确保领域的可靠性
            控制并发边界
            
        工厂
            将对象的使用和构造分离
            封装复杂实体和值对象的创建逻辑
            保障复杂实体和值对象的业务完整性
            
        仓库
            是聚合根在内存中的集合接口
            提供聚合根的检索和持久化需求
            将领域层与基础实施层解耦
            通常不用于报告需求
            
        领域事件
            业务人员所关心的事件,是通用语言的一部分
            记录聚合根的所有变更
            处理跨聚合的通信需求
            
        事件溯源
            使用历史事件记录替换快照存储
            提供对历史状态的查询
    

    3. DDD微服务架构模型

    3.1 基本架构

    3.1.1 DDD分层架构

    在这里插入图片描述

    3.1.1 六边形理论

    在这里插入图片描述

    3.1.1 CQRS架构设计

    在这里插入图片描述

    3.2 代码结构

    在这里插入图片描述在这里插入图片描述DDD四层架构说明:
    https://blog.csdn.net/m0_37583655/article/details/108396842

    3.3 服务调用

    在这里插入图片描述

    4. DDD落地框架

    目前网上对于DDD更多的是理论,下边推荐两款落地的开源框架。

    4.1 xtoon-boot框架

    xtoon-boot是基于领域驱动设计(DDD)并支持SaaS平台的单体应用开发脚手架.
    https://gitee.com/xtoon/xtoon-boot/

    4.2 Cola框架

    cola框架是阿里大佬张建飞(Frank) 基于DDD构建的平台应用框架。“让COLA真正成为应用架构的最佳实践,帮助广大的业务技术同学,脱离酱缸代码的泥潭!”

    https://blog.csdn.net/significantfrank/article/details/110934799

    展开全文
  • 什么是领域驱动模型? 是一套完整,详尽的方法论,从如何需求沟通(构建领域知识),到高层设计(战略建模),详细设计(战术建模),细致到代码的实现风格都给出了示例。 什么是领域驱动设计(Domain Driven ...
  • DDD领域驱动设计-模型

    2021-02-03 22:44:22
    模型是对特定问题或领域的关键信息的抽象,这样使我们更专注问题; 模型的作用 1、模型和实现相互影响,我们通过模型分析来最终实现产品,实现也是对模型的解释; 2、模型为团队沟通提供通用的语言; 3、模型...
  • 要想深入掌握和了解DDD 领域驱动设计的核心,那无论如何也绕不开两大较为抽象的概念——“贫血模型”、“充血模型”: 贫血模型即事务脚本模式。 充血模型即领域模型模式。 - 贫血模型 - 贫血...
  • ddd
  • 上一篇:《DDD 领域驱动设计-如何控制业务流程?》 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新,并增加了应用层代码) 在 JsPermissionApply 领域模型中,User 被设计为值对象,...
  • DDD 领域驱动设计

    2021-03-09 10:19:04
    2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software (领域驱动设计),简称Evans DDD领域驱动设计分为两个阶段: 以一种领域专家、设计人员、开发人员都能理解的通用...
  • 5、简单实现核心的领域模型,屏蔽无关基础设施和界面,进行单元测试和验证需求。 有效建模的要素: 1、模型和实现的绑定。最初的原型虽然简陋,但它在模型与实现之间建立了早期链接,而 且在所有后续的迭代中...
  • DDD领域驱动设计基本理论知识总结 Posted on 2011-10-10 01:01 netfocus 阅读(120434) 评论(82) 编辑 收藏 领域驱动设计之领域...
  • 点击上方“朱小厮的博客”,选择“设为星标”后台回复"书",获取后台回复“k8s”,可领取k8s资料- 前言 -要想深入掌握和了解 DDD 领域驱动设计的核心...
  • 贫血模型即是事务脚本模式,充血模型即是领域模型模式。 贫血模型 最早广泛应用是源自于EJB2,最强盛时期则是由Spring创造,把“行为”(也称为逻辑、过程)和“状态”(可理解为数据,对应到语言就是对象成员变量)...
  • 贫血模型 什么是贫血模型? 贫血模型就是缺血了,缺东西,也就是只有数据但是没有业务逻辑或者有业务逻辑但是没有数据。 比如你有一个计算类,他有一个加法计算的方法。但是他不持有计算的数据。 和贫血模型对应的...
  • 领域模型驱动设计(DDD)之领域模型驱动设计(DDD)之模型提炼模型提炼
  • DDD领域驱动不是架构,而是一种架构设计方法论。 在当今以微服务为主流的环境下,很多时候我们在拆分微服务的时候,往往是根据直觉、经验对服务进行拆分,比如订单服务、用户服务、支付服务。 但这些服务只是将传统...
  • 作者:JavaEdge在掘金链接:https://juejin.cn/post/6917125801460629518- 前言 -要想深入掌握和了解DDD 领域驱动设计...
  • 领域驱动设计’估计大部分人看了并不太理解,‘领域’是指建立了领域模型,‘设计’是指程序实现也就是程序编码。 领域驱动设计可以理解为:严格按照模型来编写代码,能够使代码更好地表达设计含义,并且使模型与...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 975
精华内容 390
关键字:

ddd领域驱动模型设计