赞
踩
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cainiao_user/article/details/78285385
大多数的程序员写的代码都不属于DDD,有设计的不多,更多的是像写“面条代码”。从端上一条线杀到数据库完成一个操作。设计集中在数据库(有时数据库设计都没有,一堆字段也没个注释),代码更多的是靠自我修养。这个时候就需要依赖强大的测试来保证我们的代码质量。这样久而久之就会越陷越深,陷入到技术债当中。
领域模型探讨
1. 领域模型设计:基于对象 VS 基于数据库
谈到设计,我们通常会从两种维度入手:
a. Data Modeing: 通过数据抽象系统关系,即数据库设计
b. ObjectModeing: 通过面向对象方式抽象系统关系,即面向对象。我们一开始就把整个系统的对象列出来。
大部分架构师都是从数据库设计开始设计软件系统的,少部分人是从objectModeing方式进行开始设计软件系统的。这两种建模方式并不冲突,但从哪个方向开始设计,对系统最终形态有很大的区别。
DataModel
领域模型(也叫数据模型)
好的领域模型可以让产品结构清楚,修改更方便,演进成本更低。在一个开发团队里面,架构师很重要,他决定了软件结构,这个结构决定了软件未来的可读性、可扩展性、可演进性。传统的开发模式是:架构师来设计领域模型,然后开发人员基于这个领域模型进行开发。
10多年前这个领域模型其实就是数据字典。架构师在需求讨论过程中不停演进更新这个数据字典。数据库设计是根本,一切开发都是围绕这本数据字典展开
在servicec层就是去manage大部分的逻辑,POJO作为数据在manage层不停的变换和组合。所以在manage层操作的都是POJO对象。
service层是一个巨大的加工工厂,围绕着数据库这份DNA,完成业务逻辑。
ObjectModel
2004rh ,EricEvans发表了Domain-Driver Design-Tackkling Complexity in the Heart of software(领域驱动设计),开创性的理论阐述
在聊到DDD的时候,可以做个假设,如果我们的机器内存足够大永远不当机,那我们就不需要再设计数据库了。那这个时候你会怎么设计你的软件 ?
没了数据库,那领域模型就要基于程序本身来设计了,这个时候就把设计模式的作用发挥出来了。可以充分发挥设计模式的价值了。
有人说:“类与表有点像,甚至认为表和类就是对应原,行row和object就是对应的”。其实这种认知是不对的(我之前的理解也是这样的,doamin->POJO->table)
类和表有以下几个显著区别,这些区别对领域建模的表达丰富度有显著差别,因为类里面有封装、继承、多态,我们对领域模型的表达要生动得多,对SOLID原则也会严谨
在面向对象里面我们可以设计领域模型,service层就是基于这些模型做的业务操作(它不再是万能的上帝之手了,很多动作就交给domainobjects去处理):领域模型并不完成业务,每个domain object都是完成属于自己应有的行为;就如同人跑这个动作,person.run 是一个与业务无关的行为。但在manager或service在调用some person.run的时候可能完成的100米比赛这个业务,也可以是跑去送外卖的这个业务。
它的领域模型重点是放在Domain层上。
数据库不再去承载领域模型这个包袱了,数据库回归的是persistence这个本质。它需要完成两件事情:
【存】将对象数据持久化到存储介质中
【取】高效的把数据查询并返回到内存中
由于没有承载领域建模这个特性了,数据库的设计就可以做的非常天马行空了。我们可以做列式数据库、K-V数据库,文档数据库,可以设计中间表去完成大数据查询。
数据库的设计更多的是为了存、取需求。不需要表达领域模型。
新的架构图:
public class Son{
private Father father;
public Father getFather(){return this.father;}
}
public class Father{
private Son son;
private Son getSon(){return this.son;}
}
这两个领域对象之间存在循环引用 的风险,为了解决有向无环,防止这个循环调用。可以这样操作:
public class Father{
//private Son son; 删除这个引用
private SonRepository sonRepo;//添加一个Son的repo
private getSon(){return sonRepo.getByFatherId(this.id);}
}
public class Shop{
//private List<Product> products; 这个商品列表在构建时太大了
private ProductRepository productRepo; // 这是一个持久化操作,需要操作DB的
public List<Product> getProducts(){
//return this.products;
return productRepo.getShopProducts(this.id);
}
}
回到上面的充血模型
public class Father{
private SonRepository sonRepo;
private Son getSon(){return sonRepo.getByFatherId(this.id);}
public Father(SonRepository sonRepo){this.sonRepo = sonRepo;}
}
@Component
public class FatherFactory{
private SonRepository sonRepo;
@Autowired
public FatherFactory(SonRepository sonRepo){}
public Father createFather(){
return new Father(sonRepo);
}
}
上面说了在domain里面带上了persistence之后把测试变得对DB有依赖了。那像mock/stub这些依赖是高效单元测试的基本要求。
public class Father{
private SonRepository sonRepo;//=new SonRepository()这里不能构造
private getSon(){return sonRepo.getByFatherId(this.id);}
//放到构造函数里
public Father(SonRepository sonRepo){this.sonRepo = sonRepo;}
}
由于 领域模型存在于内存对象里,这些对象最终是要落到数据库。由于不需要考虑领域模型的束缚,所以数据库设计就可以灵活多变了。
加了一层tunnel接口,通过这个接口可以实现对domain对象在不同类型的DB存取。而且respository并没有直接进行持久化工作,而是将domain对象转换成POJO再交给tunnel去做持久化工作。
今天看到一句比较鸡汤的话:你可能身处人生低谷,但不要放弃学习,不能沮丧。更不能停止前进的脚步!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。