About that thing the domain model transformation

[TOC]


Application of stratified hierarchical domain model & Why is it important?

We develop software design and development process, the software used to transverse split into several layers. Such as common three-tier architecture: presentation layer (VIEW / UI), business logic layer (SERVICE / BAL), data access layer (DAO / DAL). As shown below:
About that thing the domain model transformation

Why is it that the application system to be stratified? It is mainly to solve the following problems:

  1. The first is decoupling:

    The computer has a saying: All software problem can be solved by adding a layer. When the larger the system, the more teams, the faster the changes in demand, the more we need to ensure that fewer dependencies between procedures. The stratified / interface-oriented programming, the more we tend to make in responding to change.

  2. The second is to simplify the problem:

    When we do not understand the operation from the user data off the disk until the entire process of interaction, we should think another way. Think of layers of what support should provide, through a clear definition of the division of layers, complex question becomes how to combine the function of each layer up "building blocks to build."

  3. The third is to reduce system maintenance and upgrade costs:

    Here embodies the advantages of interface-oriented programming. We abstracted data access layer, we only need to ensure that the same interface to provide both the underlying database using Oracle or MySql, the superstructure is not perceptible.

  4. The fourth logic multiplexing / code reuse:

    By layering, layers of well-defined responsibilities, the system will no longer appear in multiple places with a database query code table. Because the query a database table will only work to unify provided by a data access layer class.

  5. The fifth team to improve development efficiency:

    If the development team a lot, defined by the hierarchical and interfaces. Each team only needs to follow interface standards / specifications development, can be developed in parallel. There is a more appropriate to describe: the hierarchical software corresponding to the transverse cut several times, corresponding to the software modularization cut longitudinally several times.

In the "Ali Baba Java Development Manual", recommended for application layering is this:
About that thing the domain model transformation

  • Open Interface Layer: Service packaging method of directly exposed to RPC interface; http encapsulated into the Web Interface; a gateway security control / flow control.
  • Terminal display layer: template layer exhibits render and execute the respective ends. The main current is velocity rendering, JS rendering, JSP rendering, display and other mobile terminal.
  • Web layer: mainly for access control forwarding, all kinds of basic parameters check, business or simple treatment no longer used.
  • Service Layer: Relative collective business logic services layer.
  • Manager层:通用业务处理层,它有如下特征:
    1. 对第三方平台封装的层,预处理返回结果及转化异常信息。
    2. 对Service层通用能力的下沉,如缓存方案/中间件通用处理。
    3. 与DAO层交互,对多个DAO的组合复用。
  • DAO层:数据访问层,与底层MySQL、Oracle、HBase等进行数据交互。
  • 外部接口或第三方平台:包括其他部门RPC开放接口,基础平台,其他公司的HTTP接口。

以上的层级只是在原来三层架构的基础上进行了细分,而这些细分的层级仅仅是为了满足业务的需要。千万不要为了分层而分层。

过多的层会增加系统的复杂度和开发难度。因为应用被细分为多个层次,每个层关注的点不同。所以在这基础上,抽象出不同的领域模型。也就是我们常见的DTO,DO等等。其本质的目的还是为了达到分层解耦的效果。


典型的领域模型都有哪些?

以上我们简单了解了分层的重要性,那么随着分层引入的典型领域模型都有哪些?我们还是来看看《阿里开发手册》提供的分层领域模型规约参考:

  • DO(Data Object):此对象与数据库表结构一一对应,通过DAO层想上传输数据源对象。
  • DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
  • BO(Business Object):业务对象,由Service层输出的封装业务逻辑的对象。
  • AO(Application Object):应用对象,在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
  • VO(View Object):显示层对象,通常是Web向模版渲染引擎层传输的对象。
  • Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。

各个领域模型在分层上的传输关系大概是这样:
About that thing the domain model transformation

在给出的参考中并没有对模型对象进行非常明确的划分,特别是对BO、AO、DTO的界限不是非常明确。这也是因为系统处理的业务不同、复杂度不同导致的。所以在设计系统分层和建模的时候,需要综合考虑实际应用场景。

数据在上传下达的过程中就会出现转换的工作,可能有些小伙伴会觉得麻烦,为什么要弄出这么多O?转来转去的多累!

在这里我举个例子,比如你查询自己网上购物的订单,可能会在网页上看到这样的信息:
About that thing the domain model transformation

其中包含:订单编号,下单日期,店铺名称,用户信息,总金额,支付方式,订单状态还有一个订单商品明细的集合。

对终端显示层来说,这些信息是可以封装成一个VO对象的。因为显示层的关注点就是这些信息。为了方便显示层展示,我们可以将所有属性都弄成字符串类型。如下示例,可以看到,除了订单id外,都是String类型:

public class OrderVO {
    /**
     * 订单id
     */
    Long orderId;

    /**
     * 下单日期
     */
    String orderDate;

    /**
     * 总金额
     */
    String totalMoney;

    /**
     * 支付方式
     */
    String paymentType;

    /**
     * 订单状态
     */
    String orderStatus;

    /**
     * 商铺名称
     */
    String shopName;

    /**
     * 用户名称
     */
    String userName;

    /**
     * 订单商品明细集合
     */
    List<ProductVO> orderedProducts;
}

再来看看对于业务逻辑层来说,它关心的是什么呢?显然跟显示层关注的不一样,它更加关注的是内部的逻辑关系。如下示例:

public class OrderVO {
    /**
     * 订单id
     */
    Long orderId;

    /**
     * 下单日期
     */
    Date orderDate;

    /**
     * 总金额
     */
    BigDecimal totalMoney;

    /**
     * 支付方式
     */
    PaymentType paymentType;

    /**
     * 订单状态
     */
    OrderStatus orderStatus;

    /**
     * 商铺信息
     */
    ShopDTO shopInfo;

    /**
     * 用户信息
     */
    UserDTO userInfo;

    /**
     * 订单商品明细集合
     */
    List<ProductDTO> orderedProducts;
}

从如上代码可以看到,下单日期使用的Date类型,金额使用BigDecimal,支付方式和订单状态使用枚举值表示,商铺名称和用户名称变成了商铺信息/用户信息对象,明细集合中的商品也变成了DTO类型的对象。

在业务逻辑层面,更多的是关注由多种信息组合而成的关系。因为它在系统中起到信息传递的作用,所以它携带的信息也是最多的。

那我们再来看看数据持久层,上面也提到了,数据持久层与数据库是一一对应的关系,而上一层的订单信息其实可以拆解为多个持久层对象,其中包含:订单持久层对象(OrderDO),商铺持久层对象(ShopDO),用户持久层对象(UserDO)还有一堆的商品持久层对象(ProductDO)。相信通过描述大家也可以理解具体的拆分方法了。

回过头来想想,如果我们一路拿着最开始的OrderVO对象来操作,当我们想要将它持久化时,会遇到多少坑就可想而知了。所以分层/拆分的本质还是简化我们思考问题的方式,各层只关注自己感兴趣的内容。


模型转换需要注意的问题是啥?

可这样的拆分确实增加了许多工作量,不同模型之间转来转去的确实头疼。那就让我们来梳理一下,在模型转换时都需要注意哪些问题。在进行不同领域对象转换时,有些问题是需要我们考虑的。
About that thing the domain model transformation

例如,上面这两个不同的模型在转换时,我们就需要考虑一些问题:

  1. 原对象和目标对象相同属性的类型不一样,有的是Date,有的是BigDecimal,还有的是枚举
  2. 属性的名称也不一样
  3. 集合类属性中的泛型也不一样
  4. 能不能只复制一部分属性
  5. 能不能自定义转换逻辑
  6. 嵌套对象是深拷贝还是浅拷贝

这么多需要考虑的地方,咱们要怎么处理,才能优雅的进行模型转换呢?


常见的模型转换方法了解下!

这里我调研了大概有10种方法,有些使用起来比较复杂就没有下大力气去深入研究,如果有感兴趣的小伙伴,可以自行深入研究下。

做为测试和讲解的案例,咱们就以上面说到的OrderDTO转OrderVO为例,来说说下面的各种方法。源对象大体结构是这样的:
About that thing the domain model transformation

我们期待转换完的OrderVO对象是这样的:
About that thing the domain model transformation

先来看第一种方法:
也是最简单粗暴的方法,直接通过Set/Get方式来进行人肉赋值。代码我就不贴了,相信大家都会。

说一说它的优缺点:
优点:直观,简单,执行速度快
缺点:属性过多的时候,人容易崩溃,代码显得臃肿不好复用

第二种:FastJson:
利用序列化和反序列化,这里我们采用先使用FastJson的toJSONString的方法将原对象序列化为字符串,再使用parseObject方法将字符串反序列化为目标对象。
About that thing the domain model transformation

转换后的结果如下:
About that thing the domain model transformation

可以看到转换后的数据格式有几个问题:

  • 日期不符合我们的要求
  • 金额也有问题
  • 最严重的是,当属性名不一样时,不会进行复制

这就是第二种使用JSON处理,好像也不能满足我们的要求

第三种,Apache工具包PropertyUtils工具类,代码如下:

About that thing the domain model transformation

转换代码看着很简单,但是转换过程会报错:
About that thing the domain model transformation

缺点:

  • 属性类型不一样,报错
  • 不能部分属性复制
  • 得到的目标对象部分属性成功(这点很要命,部分成功,部分失败!)

第四种,Apache工具包BeanUtils工具类,代码如下:
About that thing the domain model transformation

转换后的结果是这样:
About that thing the domain model transformation

缺点:

  • 日期不符合要求
  • 属性名不一样时不复制
  • 目标对象中的商品集合变成了DTO的对象,这是因为List的泛型被擦除了,而且是浅拷贝,所以造成这种现象。

第五种,Spring封装BeanUtils工具类,代码如下:
About that thing the domain model transformation

在忽略了部分属性后,转换结果就只剩下:
About that thing the domain model transformation

apache的BeanUtils和spring的BeanUtils中拷贝方法的原理都是先用jdk中 java.beans.Introspector类的getBeanInfo()方法获取对象的属性信息及属性get/set方法,接着使用反射(Methodinvoke(Object obj, Object... args))方法进行赋值。

前面五种都不能满足我们的需要,其实想想也挺简单。对象转换本来就很复杂,人工不介入很难做到完美转换。

第六种,cglib工具包BeanCopier:

cglib的BeanCopier采用了不同的方法:它不是利用反射对属性进行赋值,而是直接使用ASM的MethodVisitor直接编写各属性的get/set方法生成class文件,然后进行执行。

使用方法如下,注释写的很清楚。我们通过自定义的转换器来处理Date转String的操作:
About that thing the domain model transformation

转换结果如下,对于我们自定义处理的属性可以完美支持,其他未处理的属性就不行了:
About that thing the domain model transformation

优缺点:

  • 字节码技术,速度快
  • 提供自己自定义转换逻辑的方式
  • 转换逻辑自己写,比较复杂,繁琐
  • 属性名称相同,类型不同,不会拷贝(原始类型和包装类型也被视为类型不同)

第七种,Dozer框架:

注意,这已经不是一个工具类了,而是框架。使用以上类库虽然可以不用手动编写get/set方法,但是他们都不能对不同名称的对象属性进行映射。在定制化的属性映射方面做得比较好的就是Dozer了。

Dozer支持简单属性映射、复杂类型映射、双向映射、隐式映射以及递归映射。可使用xml或者注解进行映射的配置,支持自动类型转换,使用方便。
About that thing the domain model transformation

使用方式很简单,关键在于配置:
About that thing the domain model transformation

在配置文件中对特殊属性进行了特殊定义,转换结果符合我们的要求:
About that thing the domain model transformation

它的特点如下:

  • 支持多种数据类型自动转换(双向的)
  • It supports conversion between different attribute name
  • Supports three mapping configuration (annotation mode, API mode, XML mode)
  • It supports the configuration to ignore some of the properties
  • Support for custom attributes converter
  • Deep copy nested objects

Eighth, MapStruct frame:
based on the Java annotations JSR269 processor, a mapping relationship configured by the annotations generated automatically at compile time interface classes. Similar to the principle of Lombok as well, so on execution speed and Setter, Getter almost. I am currently using more MapStruct personal and BeanCopier, the latter will have time to write a separate article describes the use of MapStruct.

Ninth, Orika frame:
support registration code field is mapped, the mapping generated by the bytecode Bean javassist library, after loading the bytecode execution file generated directly.

Tenth, ModelMapper framework:
Based on the principle of reflection or direct assignment of member variable. Equivalent BeanUtilsadvanced version

Several other frameworks will be no in-depth study. But looking at the situation should be able to meet the requirements of the use of the actual scene. These conversion methods described in, substantially follow the performance: Manual assignment> cglib> reflected> Dozer> serialization.

In a real project, a comprehensive model conversion using the above method. DO lower layer such as less related to the nested object, change less, it can be used directly to BeanUtils. If it is speed, stable priority system, obediently use Set, Get achieve it.

Guess you like

Origin blog.51cto.com/zero01/2441196