欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

谈ApacheOFbiz会员模块表结构设计

程序员文章站 2022-03-05 14:31:12
...

数据库表的结构设计可谓是ofbiz除技术框架之外,另一个非常值得学习的方向。这篇文章我们来谈谈ofbiz对电子商务会员表的设计。 PARTY ofbiz对人、团体进行了抽象,称之为party,翻译为中文称之为“会员”(但我觉得抛开领域,如果你也有相关的设计需求,在其

数据库表的结构设计可谓是ofbiz除技术框架之外,另一个非常值得学习的方向。这篇文章我们来谈谈ofbiz对电子商务会员表的设计。

PARTY

ofbiz对人、团体进行了抽象,称之为party,翻译为中文称之为“会员”(但我觉得抛开领域,如果你也有相关的设计需求,在其他领域可能称之为团体更合适)。会员在ofbiz被设计为一个抽象的概念(对应到面向对象设计中,你可以称其为一个基类),它有两个具体的延伸(继承者):分别是PERSON以及PARTY_GROUP。数据库的E-R图:

谈ApacheOFbiz会员模块表结构设计

这里PERSON,PARTY_GROUP分别表示“个人会员”、“组织会员”。Party只是一种抽象,它定义了可以被抽象为“会员”的对象所具有的基本特征。但 “个人”以及“组织”会员却具备比 “基本会员”更多的特征,所以此处从Party延伸出两张表来存储这些额外的特征信息它们的主键都是PARTY表的PARTY_ID。
喎?http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD48aDE+UEFSVFlfVFlQRTwvaDE+PHA+cGFydHlUeXBltqjS5cHLcGFydHm1xMDg0M3UvMr4oaNFLVLNvMjnz8KjujxiciAvJmd0OzwvcD48cD48aW1nIHNyYz0="http://www.2cto.com/uploadfile/Collfiles/20141208/2014120809092365.png" alt="\" />

可以看到,PARTY_TYPE是拥有层级关系的(它的一个属性PARENT_TYPE_ID自关联了PARTY_TYPE的主键:PARTY_TYPE_ID,下面如果看到E-R图上有自关联到本身的,都表示这种关系,不再敖述)。

ofbiz提供的初始数据中有如下几种party type:

谈ApacheOFbiz会员模块表结构设计

构建成层级关系如下图所示:

谈ApacheOFbiz会员模块表结构设计

上面展示的两张表:PERSON、PARTY_GROUP也是其中的两个partyType,并且这些partyType都可以独立扩展的,PERSON、PARTY_GROUP也是仅有的两个扩展。这也是上面表结构中这两个记录的HAS_TABLE值为Y的原因。

PARTY_ROLE

就跟社会的“角色分工”一样,一个会员在系统中也必定会拥有属于自己的角色。而PARTY_ROLE表就是用于关联会员与角色类型的关系表,很明显会员与角色类型是多对多的关系(这里需要提及的是:ofbiz中只有角色类型,没有角色,或者更准确点说,角色类型包含了角色)。

谈ApacheOFbiz会员模块表结构设计

PARTY_RELATIONSHIP

上面我们看到的会员是一类“抽象”的实体。不管它表示的是个人,还是组织,它总是会跟其他会员发生关系,就好像一个人不可能脱离社会而孤立得存在着,他必然有自己的社会角色,并跟社会的其他“团体”产生联系。这在ofbiz中被抽象为“partyRelationship”。我们来看它是如果表达“关系”这个语义的:

谈ApacheOFbiz会员模块表结构设计

当你把这些所有的字段连起来,它几乎能涵盖所有的“会员关系”(要知道,有时会员关系会非常复杂,一个会员有时会存在于多个系统中)。

我们再回过头来,看PARTY_RELATIONSHIP的表结构设计:

谈ApacheOFbiz会员模块表结构设计

可以看到前五个键形成了联合主键,其中前四个都是形如XXX_FROM,XXX_TO的ID标识。表示从“FROM”方往“TO”方建立关系。其中PARTY_ID_FROM与ROLE_TYPE_ID_FROM是“源”方;PARTY_ID_TO与ROLE_TYPE_ID_TO是“目标”方。

从上面图中也可以看到,每个关系都带有两个DATETIME字段,分别表示:开始日期,截止日期。这说明关系是有“时段”这个属性的。当然,如果没有截止日期,可以看做是“永久”的。因此为了防止关系过了生效时段无法再次建立关系(因为主键不允许重复),所以选择了联合“FROM_DATE”作为联合主键(后面如果再次建立相同的关系时,只要FROM_DATE不一样,就视为一条新记录)。

这里有必要说明一下,在ofbiz的数据库设计中,大量采用了“时段”这个属性来标识记录的有效性。这样的设计与逻辑删除相比的好处是:它除了减少了删除时因为外键约束等连带关系导致的错误,还可以直接充当“历史记录”的作用,省去了对历史表的维护,当然它的缺点就是:表中的记录会比其他的设计多得多。

当然,From跟To只是为了标识两者建立了关系,却并未说明它们到底存在怎样的关系,就好像——我跟你是朋友。这句话可以拆分为三部分:FROM方:我,TO方:你,关系是:朋友。上表中用一个字段表示了关系:PARTY_RELATIONSHIP_TYPE_ID(这只是一个外键,关联着表PARTY_RELATIONSHIP_TYPE)。

在界面上新建一个关系(此处是从外部到自己的一个关系):

谈ApacheOFbiz会员模块表结构设计

PARTY_RELATIONSHIP_TYPE

该表约束了关系的类型。比如:雇佣者、朋友、父、子、管理者,E-R图:

谈ApacheOFbiz会员模块表结构设计

从图中可以看出,会员关系类型也拥有层次关系。表中还有两个特别的字段:

ROLE_TYPE_ID_VALID_FROMROLE_TYPE_ID_VALID_TO
它们用于约束这个关系的建立双方的角色。也就是说,不是任意的两个角色之间一定可以建立起某个特定的会员关系。当然这两个字段通常都为空,表示不对此加以限制。
对每个关系类型,都可以扩展以独立实现关系(被扩展后关系类型记录的字段HAS_TABLE被标识为Y,否则默认为N),在ofbiz的初始化数据中,唯一被扩展的关系类型是:EMPLOYMENT。我们来看看EMPLOYMENT关系表的实现:
谈ApacheOFbiz会员模块表结构设计
可以看到,它跟之前的PARTY_RELATIONSHIP的主键实现方式一样。因此可以把它看做是:PARTY_RELATIONSHIP_TYPE_ID为EMPLOYMENT的PARTY_RELATIONSHIP的特殊实现。
在界面上建立一个关系类型:
谈ApacheOFbiz会员模块表结构设计

PARTY_CLASSIFICATION_TYPE

为了便于管理,ofbiz对会员按各种维度进行分类,常见的分类的类型有:年收入、价值等级、产业、雇员数量等;

PARTY_CLASSIFICATION_GROUP

会员并不会直接跟分类的类型产生关系,而是跟一个或多个分类组产生关联关系。而分类组受分类类型约束。
新建一个分类组:
谈ApacheOFbiz会员模块表结构设计

PARTY_CLASSIFICATION

会员的分类相关表的关系图:
谈ApacheOFbiz会员模块表结构设计
从表的关联关系可以看出,会员分类跟分类组是多对多的关系,并且分类具有时效性。因此联合FROM_DATE作外键。
将会员划归入一个会员分类:
谈ApacheOFbiz会员模块表结构设计

CONTENT_MECH

从这张表开始,我们来看会员的联系方式相关的表结构设计,这也是一部分非常棒的设计。谈ApacheOFbiz会员模块表结构设计
这张表存储了联系方式基本信息。它引用了另一张表:CONTENT_MECH_TYPE作为外键,来表示该联系方式的类型(通常的联系方式类型有电话、邮箱、网址等)。

CONTENT_MECH_TYPE

谈ApacheOFbiz会员模块表结构设计
可以看到联系方式类型,也是具有层级结构(父子关系)的。
当我们想新建一个联系方式时,首先必须先指定想创建的联系方式的类型:
谈ApacheOFbiz会员模块表结构设计

PARTY_CONTACT_MECH

毫无疑问,地址信息只有跟会员联系起来,才能表示会员的地址。而会员跟地址是多对多的关系,理解这个关系时需要注意的是会员可以是任何团体、组织或者个人。那这里可能就会存在两个不同的会员拥有同一个联系方式的可能,比如:一个员工会员与一个该员工所属的公司会员,它们可以都存在同一个联系方式:公司的通讯地址。当然一个会员拥有多个联系方式,这是很容易理解的。所以会员标识跟联系方式标识之间是多对多的关系,并且跟前面的设计模式相似——联系方式也有时效性,比如换电话号码,换工作导致联系方式变化等,所以联合FROM_DATE作为联合主键:
谈ApacheOFbiz会员模块表结构设计
当我们选择联系方式类型为电话号码时,会出现如下的表单填写:
谈ApacheOFbiz会员模块表结构设计
如果你新建一个联系方式的类型为电话号码,那么电话号码存储在何处?此处又跟前面谈到的HAS_TABLE字段有关(CONTACT_MECH_TYPE中也存在这个字段)。正常情况下,联系方式关联着联系方式类型,普通的联系方式的具体信息存储在CONTACT_MECH的INFO_STRING属性中。但有些联系信息不是单纯的像邮箱这样只是一个字符串,比如像电话号码、邮政编码…它们都有具体的格式表示。所以这些特例用INFO_STRING这一个属性存储也不方便,因此可以独立扩展该CONTACT_MECH_TYPE(将其HAS_TABLE字段设置为Y,这样查询该CONTACT_MECH信息的时候,就不采用INFO_STRING字段,而是采用扩展表中格式化的联系方式)。

CONTACT_MECH_PURPOSE_TYPE

当我们点击上面界面的保存按钮之后,会更进一步得扩充联系信息:
谈ApacheOFbiz会员模块表结构设计
在ofbiz中还存在一个称之为“联系目的”的东西,它是什么意思?
谈ApacheOFbiz会员模块表结构设计
看到选项我们就会明白,说白了一个人的地址簿或者电话簿中的联系方式可能有很多。它们没有主次之分,只有目的不同。

PARTY_CONTACT_MECH_PURPOSE

上面谈到了联系目的,那么很自然它需要跟会员具体的某条联系信息关联起来才能称之为:某个会员为了某种联系目的存储了一个“联系方式”记录。
谈ApacheOFbiz会员模块表结构设计
这里需要注意的是,它并没有跟PARTY_CONTACT_MECH产生直接关联(没有外键关系),而是把PARTY_CONTACT_MECH的三个主键照搬过来,联合CONTACT_MECH_PURPOSE_TYPE_ID形成四个组合主键,这是因为PARTY_CONTACT_MECH的联合主键机制无法被其他表当做外键引用。因此,可以将PARTY_CONTACT_MECH_PURPOSE看作联系信息模块的聚合。这个怎么来理解?其实一个地址可以看成:某个会员(PARTY_ID),出于某种目的(CONTACT_MECH_PURPOSE_TYPE_ID),在某段时间内(FROM_DATE),保存了某个联系方式(CONTACT_MECH_ID)。这种联系方式的设计非常有弹性,因此在大部分情况下,这种抽象性能够涵盖大部分应用场景。

CONTENT_TYPE

会员内容的设计跟联系方式类似。会员可以有一个类似文件空间在服务器上,可以供其保存文档、图片之类的东西。CONTENT_TYPE限定了会员可以存储的内容类型:
谈ApacheOFbiz会员模块表结构设计

CONTENT

该表是它的具体存储内容的地方,当然并不是唯一的,如果CONTENT_TYPE有一条记录的HAS_TABLE值为Y,则那个记录对应的表也用于存储内容。内容表里的字段非常多,就不截图了。
跟之前的联系信息类似,会员可以有多个内容,一个内容也可能从属于多个会员。因为会员是个抽象的概念,对应到实体上可能会有重合,所以需要一个“目的”来修饰会员内容,它就是——PARTY_CONTENT_TYPE。

PARTY_CONTENT_TYPE

用于修饰会员内容的用途,当然这里它的表名叫type,事实上从数据记录来看,来时充当了目的的作用。
谈ApacheOFbiz会员模块表结构设计
内容还跟其他一些表有关联(主要是被引用关系,比如:CONTENT_ROLE等),此处因为跟本文主题没太大关系,所以不再敖述。

总结

更高的抽象级别

ofbiz party模块的设计,正如它所应用的场景:非常适用于电子商务系统会员信息相关的设计。当然ofbiz中其他相关的多个系统也同样应用了这些表结构,这也意味着它有适用于一般行业、系统的通用性,这得益于这种设计的抽象级别比较高。它可以描述任何的组织、个体、他们的地址信息、他们之间的关系。特别是对会员“relationship”表的设计非常类似于《分析模式》中谈到的责任模式:
谈ApacheOFbiz会员模块表结构设计系统的用户,又或者一个非常大的跨国公司,拥有:总部、区域销售办公室、办事处、分公司等各种组织形式时,这种设计就会派上用场。

数据库表的继承关系

从PARTY、PARTY_TYPE、PARTY_GROUP、PERSON这几张表我们可以学习到数据库表的“继承”设计。

时效性设计

不是真删除、也不是逻辑删除、而是失效(FROM_DATE, THUR_DATE)。这种方式可以代替“操作-操作历史”的多表设计,转而合并为独立的一张表。