(6) 基于领域分析设计的架构规范 - 关于重构与落地

本系列目录:架构

  1. 改变与优点
  2. 领域分析基础
  3. 读写隔离
  4. 充血模型之实体
  5. 充血模型之Service
  6. 关于重构与落地

不论你们是否定可我在这里提到的这一套小规范,既然你能看到这里,仍是由衷表示感谢。核心内容已经介绍得差很少了,接下来仍是看两个很实际得话题。框架

DDD落地之殇

DDD这类架构真正落地困难,我以为,有几个缘由:分布式

  1. 规范多,尤为是充血模型,须要对业务有更清晰的认识
  2. 代码结构相比无状态扁平化开发,层次深,模块划分更严格,因此,至少从文件数上来讲,是更多的(代码行数却是不必定多)
  3. DDD并非一个“刚性”需求,若是不按这个规范来作,系统同样能够跑起来,因此,不少规范处在一个“模糊”的界定上,让开发人员没法肯定到底该如何规划代码。

正由于如此,即便团队技术Leader了解DDD,却在项目一开始的时候,因为考虑到DDD的规范过多,不便于协做,为了快速开发迭代,每每继续沿用最通用的写法。而一旦到了中期,业务复杂度上来了,当以为领域分析有用武之地的时候,低头一看,系统的复杂度已经到了难以轻松驾驭的程度,更别说来一个180度的大转变了。测试

真的,这很现实,就是一个恶性循环。优化

因此,咱们须要找到一个破冰的办法:编码

  • Leader足够强,团队成员实力不错,也给予Leader充分信任,那么在一开始就采用DDD
  • 在一开始依旧采用非DDD架构模式,但利用迭代的方式,将代码逐渐变成DDD

这二者没有绝对好坏,虽然说第一种不常见,但依旧是我挺期待和欣赏的作法。.net

而可能更实用的是第二种,但新的问题又来了,如何逐步迭代改进成DDD,从哪里入手?设计

从现有代码迭代改进

咱们不强求一步到位,但求一步步作出一些恰当地整理code

如下给出一个按部就班的参考方案:对象

1. 读写隔离

这个过程几乎不与业务挂钩,咱们将OrderService,ProductService中的查询方法剥离出来,放在诸如OrderFinder,ProductFinder之中,具体编码细则规范参见以前的章节。 整个过程,通常来讲,只要编译能经过,代码就不会有什么问题。

2. 提取Factory

在查询方法被剥离以后,就只剩下增/改/删了,其中又是最特别的,前面在讲工厂的时候提到过,因此将一个实体的建立行为提取到一个XXXFactory中,也不困难,稍微留意一下,也不容易引起业务问题。特别是在一个已经相对复杂的系统中,若是无法一会儿分析透彻聚合根的归属问题,那么能够每个实体都加一个Factory,以后再逐步合并,虽然说工做量稍大一点,但对于已经有点棘手的项目来讲,也算是一个快刀斩乱麻的办法。

3. 提取{Action}Service

也就是提取咱们前文所提到的,跨多个聚合的一个事务操做 到一个独立的Service中。 或者还能够将要求下降一点,只要以为代码量特别大(我我的以为150行以上就很大了,况且有些还带了私有方法)的一个方法,都迁移到一个独立的Service中,并采用前文提到的{具体操做}+Service的命名方式,一个Service只负责一个复杂的方法。虽然说不会很是精确,但重构后的效果仍是会很明显的。

4. 创建领域聚合体系

经果上面几步操做,我相信你的代码里,曾经那些OrderService,UserService,已经只剩下相对简单的一些改/删操做了,其实,某种程度上,他们已经逐步出现了领域聚合对象的样子了。因此,若是你作到这一步,暂时中止了,也已经值得庆祝了!

那么若是还要继续前进一步呢?小项目还好,对于大项目,会有些难了。我相信这时候OrderUser颇有多是被POJO/DTO存在的,为了保证平滑过分,咱们确定只能新建领域模型,那么可能会要采用诸如OrderDomain来表示了。而后再将以前OrderService里的方法迁移过来。

但若是你用的是Spring或者相似Spring的IOC框架,会有一个很现实的问题。因为实体是被ORM框架new出来的对象,是脱离IOC容器管控的,因此若是你要在一个实体中使用一些IOC容器里的组件,可能得要本身去经过IOC容器的上下文来手动获取,好比经过ApplicationContext来获取,而不能经过@Autowired来自动注入了。

目前我在工做中,借用版本迭代的机会,逐步执行第一步第二步,确实也是因为项目太大太杂,即便这两步走起来工做量也不小,还要和同事协调好这种新作法沟通协做问题,确实这也是挺现实的一个问题~

关于版本迭代

惟一不变的就是变化。

咱们常常得要“笑着”对着甲方或老板说:“咱们会拥抱变换”,呵呵~ 很苦,但得要面对啊。

因此,假如咱们有一个领域实体为订单明细,你们能够很容猜到,它也是属于订单聚合中的一部分,而非聚合根。订单明细的变更,都来源于订单的操做,好比订单编辑,order.modify()

某一日,产品需求发生变化,说:

  • 咱们的订单明细要单独展现一个管理页面,而不仅是像之前那样,只能从某一个订单点击查看“详情”才能看到
  • 同时,咱们要能对订单明细进行修改,而且将这些明细的订单绑定关系进行修改

这时,你会很明确的发现,曾经的订单明细已经开始“昂首抬头作主人”了,再也不“寄人篱下”了,由于它的查看入口再也不附属再其余实体上,并且咱们还得精确找到某一个明细进行修改。 独立修改

产品既然这样设计,意味着订单明细在整个业务系统中的身份已经发生变化,它的独立性也将愈来愈强,将来不少功能颇有可能将围绕它而展开。因此,订单明细将成为一个独立的聚合,进而咱们会作出一些变化:

  1. 订单明细将有本身的 OrderDetailFactory
  2. 订单明细将逐步创建本身的方法,出现如 orderDetail.modify()
  3. 曾经订单聚合中的一些操做,要迁移到新的Service中,由于他们是不一样的聚合了,有些操做已经属于跨聚合操做

其中第(3)步,是难度最大的,由于要改动之前的代码,甚至要动到业务,不只有开发难度,还有测试难度。

可是很遗憾的是,任何重构都是这样的。不论用什么框架,到必定阶段,咱们都会面对这个问题。不在开发期间进行优化,那技术债务,只会变成成千上万的BUG来给咱们上课。有的时候咱们总吐槽本身又拿到的一个前人的项目,说代码多么烂。可是反思下来,如今本身手头的代码,重构过吗?是否是也会成为将来人的笑柄呢?

其实这个将来人可能很近,可能就是一个月后的你本身。(神情复杂的笑容)

关于下一阶段的内容

目前我更多的以单计应用的架构为例,还未说起太多分布式相关的内容。

分布式是一个双刃剑,与这个架构的协调,我还在梳理中,等好了,我会第一时间发出来。

其实我不在意阅读量有多大~ 我把这个看成一个我本身的工做记录,就行了~ 谢谢你们~

相关文章
相关标签/搜索