什么该建成对象
对象、属性、关系怎么选;事件对象、联结对象与粒度;事实只存一份。

本课目标
读完这一课,你将能够
- 判断一件事该建成对象、属性、关系还是接口
- 认出事件对象和联结对象,选对粒度
- 用「事实只存一份」处理冗余,知道什么时候可以有意复制
先问:它是对象、属性还是关系
前四节讲的是怎么写,这一节讲该不该写。建模要建的是现实里的东西,不是某个系统里的表。一行 ERP 导出的数据有 order_no、customer_name、sku、quantity,说的至少是三样东西:订单、客户、产品。它们在示例工厂里是 salesOrder、customer、product,再加一个记数量的 orderLine。
对象类型
有自己的身份和生命周期,别处要引用它,人要对它做动作。例:workOrder、inspection。
属性
只是对象的一个值,不会被单独引用。例:workOrder.plannedQty。
关系
两个对象之间的一个事实。只带一两个值,就是带属性的关系。例:customerOrders、productMaterials。
接口
多个类型共有的形状,用来统一查询。例:Schedulable。
拿不准时问三句:谁会单独引用它?它会有自己的状态和动作吗?它描述的是一个东西,还是两个东西之间的事?一份数据里有几个东西,就建几个对象类型,不要照着表的列,一列建一个属性。
事件、联结与粒度
事件对象:一行数据如果是关于某个东西的一次观察,那么东西和这次观察是两个对象类型。inspection 是关于工单的一次检验,defect 是检验里发现的一个问题,有自己的状态,还有 flag-defect、close-defect 这样的动作;它们也会被别处引用,所以不是 workOrder 上的属性。相反,machine.vibration 是时间序列属性:读数可以多到每天成千上万个,没有身份、状态和动作,不必每个都是对象。
联结对象:关系带着自己的内容时,就把关系建成对象。订单和产品是多对多,但 orderLine 有自己的主键、数量和单价,还可能被单独取消,所以它是对象,两头各连一条多对一的关系。物料清单不同:productMaterials 只有一个 quantityPerUnit,没人单独引用它,留作链接属性就好。
| 带属性的关系 | 联结对象 | |
|---|---|---|
| 适合 | 只带一两个静态值 | 有自己的身份、状态和动作 |
| 例子 | productMaterials | orderLine |
| AIDC 今天怎么写 | createLink 只写两端的主键 | createObject 等规则可以读写它的全部属性 |
粒度:同一句话在不同层次上答案不同。「本月订单量」是数 salesOrder,还是加总 orderLine.quantity?把每一层建成各自的对象类型,并让名字说清楚是哪一层,别让两个人走两条路,得到两个数。
还有一个坑:不要给版本建对象,比如 workOrderV1、workOrderV2。一张工单就是一个对象,改动的历史在编辑历史里。
aidc semantic edits-history workOrder --pk WO-2609-014 --previous
事实只存一份
复制会过期。在销售订单上多存一份 customerName,客户改名以后就有两个名字;手工维护的 lineCount,每次增删订单行都要跟着改,哪个 Action 漏了一次,数字就一直是错的。原则是:每个事实存在它语义上所属的那个对象上,别处沿关系去读。
{
"name": "lineCount",
"type": "integer",
"title": "Order lines",
"derived": { "linkPath": ["lines"], "aggregation": { "type": "count" } }
}
那什么时候可以有意复制?派生属性是读时算的,不能过滤、不能排序。需要按它过滤或排序时有两条路:先过滤对象集再聚合,或者有意识地存一份。存副本要写下三件事:为什么存、以谁为准、怎么同步,尤其是所有会改它的 Action 都要一起更新。
规模也是理由:一次查询涉及一万个对象以内,派生属性放心用;超过以后先测延迟,再决定要不要存副本。这应该是一个有记录的决定,而不是默认做法。
下一节是设计评审清单:命名、状态、八个反模式,和上线前的验证。
要点
- 从业务里的东西出发建模:一份数据里有几个东西,就建几个对象类型。
- 有身份、状态或动作的建成对象;纯读数用时间序列;一个值用属性;一个事实用关系。
- 关系有自己的内容、要被引用或被 Action 读写时,建联结对象;只带一两个静态值,留作链接属性。
- 事实只存一份,副本要有意识地存,并写下原因、以谁为准和同步方式。
练一练
给一份数据做取舍
先用纸和笔,再写成定义。
一行 ERP 数据有这些列:order_no、customer_name、customer_tier、sku、sku_name、quantity、carrier、shipped_at。把它们分到示例工厂的对象类型上。carrier 和 shipped_at 归谁?
对「缺陷」「设备的一次振动读数」「供应商对某种物料的供货提前期」各做判断:对象类型、属性、时间序列,还是链接属性。写下理由。
在你自己的模型里找一个手工维护的计数,或者一个复制来的名字,改成派生属性或关系,写出定义。
小测
选一个答案,马上看解析。
Q1inspection(检验)该建成什么?
一张工单可以被检验多次,每次有结果,还会带出缺陷。它是关于工单的一次事件,要单独引用,所以是对象。
Q2productMaterials 只带 quantityPerUnit,没有别的内容。最合适的建法是?
没人会单独引用这一行,也没有状态和动作,多对多加一个链接属性最简单。联结对象留给有自己内容的关系,例如订单行。
Q3为了列表好看,把客户名字复制到销售订单上。最大的问题是?
同一个事实存了两处,就要靠人和 Action 保证同步,漏一次就出现两个名字。要展示名字,沿 customer 关系去读。