页面数据同步与领域驱动设计

前言

最近,正在做一个新需求:需要在已有的页面中新增一种与当前数据结构完全不同的数据展示,页面展示基本不变,但后端返回数据结构与这个数据代表的意义完全不同。在产品与UI眼中,这或许并不是什么大的变动,但对程序来说却比较复杂,所有牵扯到的逻辑都需要梳理。(这或许也是产品和研发打架的原因之一)

问题分析

原因

一般小型项目都是 接口 -> 数据库 -> 界面展示 一把梭,只用一个类来定义对象,中间不会经过任何转化和分层。一般来说数据结构很少变,而且多数据类型同一个页面展示的情况大多比较稀少,所以一般都是如此处理的。

解决方法

我这边一般想到几个方法:

直接修改逻辑,增加处理逻辑

这样一般改变逻辑较多,牵扯到逻辑判断的地方都要增加新数据结构的处理。

在已有数据对象中包含新对象

在已有对象中创建一个字段,将新对象注入,改写 get 类型方法,返回新对象的数据,展示到页面上。

这样改动比较简单,但容易发生数据访问问题,需要多测试才行。而且侵入性太强,需要更改已有数据结构,导致逻辑混乱。

复制页面,不同的数据结构访问不同的页面

这种方法只适用于整体数据结构改变的方式,如果是局部异同,则无法解决。而且会增加界面,增加逻辑复杂度,如果界面逻辑更改,则需要两处都进行更改。

提取公共字段,增加抽象层,增加专用展示对象

具体操作其实和直接更改类似,都是需要同样的工作量。而且由于增加了许多兼容性的处理,会导致工作量更大。

好处是,兼容性足够强,再次增加逻辑方便,而且抽象提好了数据层次,对数据结构侵入性没有。

坏处是,之前所有逻辑都需要更改数据结构,添加此层次。

将新对象转化为旧对象

这种方法一般是最容易实现的,但是如果需要进行空字段的处理,同样会增加工作量,比较麻烦。在整体逻辑需要用到某些数据的时候,如果没有此数据,则需要增加专用的处理逻辑。如果需要更改的地方比较少,工作量不太的话,此种方法则是一般情况下大多数人会选择的,但是会导致一些认知混乱,当然如果逻辑本身不复杂的话,一般不会出问题。

DataCache

https://github.com/etby/DataCache

这是一个对象缓存框架,按理说应该是一个对象池。

http://blog.etby.org/2018/03/27/datacache-library/

这是我在写发布这个库的第一个版本之后,写的一篇简单介绍的博客。

应用场景

在做一个应用的时候,经常会遇到各种页面之间数据同步的问题。这个基本是每个APP开发工程中都会遇到的问题,解决方式也是各式各样。

在这里说一下我的方法:

  1. 使用 DataBinding
  2. 数据对象全局唯一 ( 使用DataCache库 )
  3. 在所有外部入口,比如 API、DB 都会设计一层拦截层,在这次获取字段改变,然后直接使用全局唯一对象的set 方法进行设置,UI 就会自动更新

以上是核心逻辑,在整个应用中保持单个数据只使用一个Java对象,然后使用DataBinding框架触发界面更新。( 现在则可以增加LiveData,使整个过程更流畅)

这个方式并不优美,但是挺好用的。对于掌控整个数据流来说,只需要掌控入口,工作量会小很多。

一些问题

  • 由于数据对象有可能循环引用,所以对直接传递对象和序列化有影响,处理起来比较麻烦
  • 外部数据更新比较麻烦,对于API,我这边是修改了Gson源码来做到自动解析的。
  • 性能问题,虽然只用一个对象能够减少内存使用,但更新的时候通知的地方也比较多,有可能会导致性能问题。

DDD 与 DataCache

领域驱动设计虽然之前自己查了很多资料,也看了一些书,但始终不得要领,而且无法进行实际的运用。毕竟增加了许多工作量,我却无法灵活使用,不熟的东西还是不适合使用。

处理上面提到的增加页面数据的问题时,我想到了 DataCache 库,想到了它的优缺点和我现在的窘境,之后想到的 DDD 的分层。对数据结构的分层:VO/PO/DTO 等等。

DDD 的优缺点

  • 分层、解耦
  • 对逻辑更改的容忍度比较好 ( 只需要在其中一些层进行更改, 这也是所有分层设计都具备的优点 )
  • 对数据影响力的限制 ( 应用=数据+算法, 但如果数据的实用性和扩散度太高, 会导致逻辑(算法)被牵制 )
  • 穿透性有影响,会影响一些代码的通用性,增加工作量
  • 学习和理解起来比较困难

使用 DDD 的一部分

虽然 DDD 比较庞大,但其实我们已经有意无意的使用了类似的思想。比如上面页面问题提出公共数据对象的方式,或者聚合数据对象的方式,都是表现层分层的一种思想,那些对象已经类似VO了。

我们则可以做的更好,将VO完全独立出来,然后使用DataCacheVO单例化。这样就可以解决多页面的数据同步问题,在逻辑需要用到数据的时候则可以使用真实数据对象来进行操作。这样就可以避开单对象的循环引用的问题,序列化和传递对象则不必太拘束。

逻辑层和展现层使用完全不同的数据结构,当逻辑改变时,则就不需要进行大量的更改,只需要在层与层之间进行一些转化。或许这样做之后,增加的学习成本和工作量则不再是阻碍了。