外键映射
- 我的思考:外键映射适用于:1:1及1:N的关联关系,通常让非root实体持有root实体的标识域,比如唱片持有作者的标识域,曲目持有唱片的标识域。
- 项目实践:社区系统中的“帖子”实体持有“用户”实体的标识域,在数据库中则表现为Post表持有一个userNo字段。
关联表映射
- 我的思考:外键映射适用于:N:N的关联关系,关联表通常对应一个值对象。关联表通常存在两个方向的查询入口,这两个入口跟关联表外键对应的实体表有关,那个在DDD中,该关联表就可以同时属于两个“聚合”中。比如用户体系系统中“用户账户关系表”(UserAccount),作为值对象,持有userNo和accountNo;存在根据userNo查询accounts的场景,也存在accountNo查询UserAccount的场景;可以看出UserAccount属于User和Account这两个“聚合”中。
- 项目实践:用户体系系统中“用户账户关系表”,作为值对象,持有userNo和accountNo。
单表继承
- 模式概要:将类的继承层次表示为一个单表,表中各列代表不同类中的所有域。
- 我的思考:书上列出了如下优点:只需关注一张表,对象继承层次更改时无需更改存储层;缺点也很明显:数据库空间浪费,维护成本增加,可扩展性差。实际的项目经验表明,最好少用这种模式,比如在社区系统中,多个渠道发布的评论属性不一样,开发人员将多个渠道的评论属性整合放入到一张表中,且文档注释不全,导致后续开发人员踩坑,在写程序时不清楚每个字段的实际适用渠道,维护起来非常麻烦。而且后续某些渠道下线后,该大表中多余字段仍然保留,造成了极大的空间浪费。
- 项目实践:社区系统中的评论表,存储来自多个渠道的评论内容,且缺乏注释,业务代码混乱,导致难于维护。
类表继承
- 模式概要:将各个子类的公共属性放入一张父表中,子类的非公共属性放入各自的子表中。
- 我的思考:该模式最大的优点在于:领域模型和数据库结构之间关系一致,利于理解;缺点在于:读取一个对象需要多表访问,性能上需要注意。
具体表继承
- 模式概要:每个子类对应一个具体表,多个具体表之间可能存在相同的字段。
- 我的思考:该模式最大的优点在于:相比“类表继承”获取完整对象时,无需表连接查询;每个表都是自包含的,无不相关域;缺点在于:领域内全局主键较难处理;类继承层次该表对表结构影响较大。全局主键的问题可以考虑使用:Id+type这种联合主键的形式来解决,每个子表使用独立的seq,每个子表定义一个类型字段,映射到模型时,使用Id+type作为全局唯一主键;另外一种方案是:使用全局的seq,所有子表共用一个seq,坏处在于每个子表id的不连续性;公共seq可能成为资源瓶颈。
- 项目实践:用户体系的**账号表和理财账号表就是使用这种模式来建表的,当然现在是对**账号和理财账户进行独立建模,所以影响倒不是很大,但是此种模式的一个缺点较为明显:每次需要查询某个用户下的所有账户列表时,需要同时查询**账号和理财账号表,如果使用“类表继承”模式,则可以避免多表查询,因为在这个查询场景下,仅仅需要查询账户的基本信息:账号、开户日期,这些属性是所有账号共有的,因此完全可以从父表中获取。
资源库
- 模式概要:协调领域和数据映射层,使用类似于集合的接口来访问领域对象。
- 我的思考:资源库repository是一个很常见的术语,它可以理解为一个存储层的gateway,也可以理解为一个存储层的facade,repository提供面向对象的查询原语,可以参考elasticsearch等内存数据库的api实现。在存在多个存储介质的系统中个,可以使用repository包装多个存储介质的实现,比如整合redis缓存和oracle主存配合使用的数据存取过程;还可以有基础自repository的哑实现,使用领域模型不必依赖于存储层也可以快速完成建模。
- 项目实践:用户体系和社区系统中均有使用到,使用场景见上面描述。
- 模式概要:经典的MVC模式,将‘视图(View)’、‘模型(Model)、‘控制器(Controller)’分离。
- 我的思考:MVC是非常经典的设计模式,从struts到springmvc,研究springmvc足矣,囊括了filter、inteceptor等基本组件。
- 项目实践:web类项目基本逃不掉MVC,不再赘述。