大家好,小弟有一个关于订单系统中订单的疑惑,能否给我点指导,谢谢


有一个租车订单管理系统,核心是订单,一个订单允午有多部车辆,订单也会有跟单员和业务员,为了及时能联系客户,也会需要乘客信息(有多个乘客)

订单中包括以下元素:
1、租车客户(Customer)
2、订单的跟单员(User)
3、订单的业务员(User)
4、客户需要租的车辆(Car)
5、乘客信息(passenger)
6、行程,及备注信息

我写了如下的类:

public class Order {
/* 订单标识, */
private String orderEntityID;
// 订单编号
private String orderNumbers;

// 订单的客户
private Customer customer;
// 跟单员
private User Merchandiser;
// 业务员
private User Salesman;
// 订单所使用车辆
private Set<Car> cars;
// 乘客(姓名+电话)
private List<Passenger> passengers;

// 订单行程
private String orderTravel;

// 订单行程
private String orderRemark;

/**
* 带唯一标识的构造器
* @param orderEntityID 实体标识
* */
public Order(String orderEntityID) {
this.orderEntityID = orderEntityID;
}

///------这是getter和setter

}

我的疑惑是:如果以这种方式写出这个订单(Order)类,以后会出现很大的问题
1、如果车辆的信息有变动,修改车辆时,已经归档了的订单,因为有Set<Car> cars;这个关联,那么订单相关车辆的信息也会跟着改变。(按正常的实际需求是,改动车辆,归档的订单不会受到影响,除非用户去重新更新了这个归档的订单)
2、同1一样,跟单员、业务员的修改也会影响到归档了的订单
3、当用户删除了跟单员(这个员工不在公司做了),以前归档了的订单也受些影响

为了解决上面的问题,我又修改了一下:

public class Order {
/* 订单标识, */
private String orderEntityID;
// 订单编号
private String orderNumbers;

// 订单的客户
private Customer customer;
// 跟单员
private User Merchandiser;
// 业务员
private User Salesman;
// 订单所使用车辆
private Set<Car> cars;
// 乘客(姓名+电话)
private List<Passenger> passengers;

// 订单行程
private String orderTravel;

// 订单行程
private String orderRemark;

//------增加部分-------------------------------------
private String customerName;//把客户名称从客户(Customer)拿出来
private String MerchandiserName;//跟单员(User )名称,联系电话等信息,也从User 复制到Order中来,
为了更清淅,我就不列出来了所有包含的元素了
//这里是把订单包含的元素像上面两行一样复制出来
//这样的话,就算是客户改动了,也不会关联到这个归档了的订单,因为这个订单保留了当时下单那时的信息

/**
* 带唯一标识的构造器
* @param orderEntityID 实体标识
* */
public Order(String orderEntityID) {
this.orderEntityID = orderEntityID;
}

///------这是getter和setter

}

这样虽然可以,但是我想了想也会出现很多问题,同时我也觉得这样做,或者这种方式可行吗?
具体的问题如下:
1、如果用户按客户名称搜索时,由于有些客户有改动过名称之类的,归档的订单没办法出来
2、如果按跟单员(由于跟单元,有名称后来被修改了,归档的订单又没更新,也找不到呢)

像这类问题应该如何解决呢
[该贴被freesea于2010-09-16 19:27修改过]

各位帮个忙了,在这谢了

我认为你这样归档是对的,相当于保留订单的一个快照
至于你最后的顾虑,你可以在归档表中加入客户ID和跟单员ID
根据客户名称和跟单员名称搜索归档信息时,名称是来源于客户表和跟单员表的。而实际数据从归档表中获取。
上面是根据当前最新名称进行检索的。

假如名称条件是允许历史+当前,那么就是用union来合并结果集
不过hql没有这个关键词,只能用sql

2010年09月17日 12:48 "rayo"的内容
我认为你这样归档是对的,相当于保留订单的一个快照
至于你最后的顾虑,你可以在归档表中加入客户ID和跟单员ID
根据客户名称和跟单员名称搜索归档信息时,名称是来源于客户表和跟单员表的。而实际数据从归档表中获取。
上面是根据当前最新名称进 ...

我也是想打算这样做的,只是想了解大家有没有其它方法,因为这些对象的编写是非常常见的,对象的关联问题,就如我上面写的,一个对像引用了多个对象,及对象集,如果每个被引用的对象都要复制出它的属性,这样是不是很不自然?如果不这样写,是否还有更好的方式,或者我本来就理解错了这种对象关系?所以就是这里一直困扰着我,谁能提供些指导呢,真是感激不尽哦
[该贴被freesea于2010-09-17 14:45修改过]

如果都把关联的对象的属性复制到Order对象,以减小竞争的话,是不是对Order污染了呢,有没有更好的解决方法,希望大大们指点一下

Order 类,目前只把客户(Customer)的主要属性提到Order中来,以减小竞争,同时还保留Customer的引用,Order类中还有其它比如跟单员,等等还没提出来,我感觉这种方式很别扭,也不知道如何改进,希望高人指点一下,谢 谢


public class Order {
/* 订单标识,经历各种生命周期,始终不变 */
private String orderEntityID;
// 订单编号
private String orderNumbers;

// 订单的客户
private Customer customer;
private String customerName;
//公司名称
//从客户中选择的秘书复制到订单中来
private Collection<CustomerContact> contacts;
private String companyTel;
//公司电话
private String companyFax;
//公司传真

// 跟单员
private User Merchandiser;
// 业务员
private User Salesman;
// 订单所使用车辆
private Set<Car> cars;
// 乘客(姓名+电话)
private List<Passenger> passengers;

// 订单行程
private String orderTravel;

// 订单行程
private String orderRemark;

/**
* 带唯一标识的构造器
* @param orderEntityID 实体标识
* */

public Order(String orderEntityID) {
this.orderEntityID = orderEntityID;
}

下是Customer 类


public class Customer {
private String customerEntityID;
private String customerName;
private String industry;//客户行业
private String area;
//所在区域
private String customerType;
//客户类型

/*客户秘书可以有多于1个的联系人*/
private Collection<CustomerContact> contacts;

private String companyTel;
//公司电话
private String companyFax;
//公司传真
private String companyWeb;
//公司网址
private String companyAddress;
//公司地址

private String customerSource;
//客户来源
private String remark;
//备注

public Customer(String customerEntityID){
this.customerEntityID = customerEntityID;
}
}

呵呵。。。当然是业务分析的有问题。车辆变了是车辆变更的业务问题,跟订单有什么关系呢。车辆报废了或者换号了,但不代表租车记录变了啊。同样,离职人员也不能把记录删除了啊,可以做个离职的标记就可以了。

如果没有关系,那归档的订单因为与车辆有关联关系,当车辆改变时,如何体现已归档的订单呢,已归档的订单本来是不能改动的,由于它的一个关联改了

这是不变性和唯一性的问题吧,把会变动的东西关联起来当然会出现不良后果。

其实订单是关键,可以先从订单开始想起,订单的唯一标识是什么?
几种方案:
一、订单号
订单号是一串码,有唯一性和不变性,可以把他跟你需要查询的对象绑定起来——也就是把订单号,作为一个集合保存在一个客户的字段中。

二、客户ID
客户ID,明显具有唯一性和不变性,名字的话当然会有可能重复和改变的,然后可以把多个客户ID保存在订单的一个字段中。

三、订单号和客户ID
两者结合起来,建立第三个表,用来关联。

搜索客户名,这明显是不可能准确搜索的,因为名字本来就不是唯一性——同名的多的是。
跟单员也是同一道理,为什么很多表都设立ID字段也是为了唯一性,而ID基本不予以改变的,隐含不变性。

我这里只是教你想问题的方法而已,至于你的具体如何实现,真的不是一两句话可以表达所要考虑的东西。

还是用领域模型的概念来分析一下吧。一个领域模型是否正确,就是看它能否在不同场景中适应变化。如果出现了矛盾,这就说明你的领域模型有问题,其中最重要的是分析变和不变的问题。车辆变更是正常的,租车的历史记录不变也是正常的。可是现在出了矛盾,显然问题出在处理车辆变更的业务模型上,分析一下:对订单而言,不变的是“那天租了一辆某厂某年生产的出厂编号为某号的车”,而对车辆管理而言,变化的是“某厂某年生产的出厂编号为某号的车改号了或者报废之类的”。所以问题出在了车辆管理上,再具体的你就想想吧。

似乎楼上的都没有准确的回答楼主的问题,对于小型管理系统,这是很常见的问题。我所见到的许多人做的许多系统中,要么是客户对历史数据没太多的要求,要么是程序开发人员找不到好的解决方案而忽略这个问题。
但是问题仍然存在,管理系统中任何实体都是有状态的,人员休假、离职,车辆维修停用,报废都有个状态,即使消失了也要保留,不能物理删除。这些不是问题,关键是下面的问题。
历史和当前两种状态的关系,签订订单时,车辆、人员都是有不同的状态(或属性),但是以后很难保证它们的状态不发生改变(车换了驾驶员、车重新喷漆换了已中颜色、员工调换部门、改名等等)。这些都是我们无法保证的,况且设计人员在设计时不能假设那些是不会变化的,或是几年内、系统维护期内某些数据不会发生变化。一旦在订单后实体数据变了,就会导致订单与实体不一致。而我们的客户又偏偏需要签订订单时的原始资料。
以前我使用过的一种方案是,存储历史数据。楼主说,订单是需要归档的,这真好可以使用历史数据。订单本身存储的未归档的内容,并且不需要添加快照字段。令行建立一个Bean(存放历史数据的东西,我们就叫它历史订单)。历史订单包含了订单、车辆、跟车人、业务员的全部属性(ID、编号。。。),在每次归档操作时会将当时的订单和它相关对象的数据存在历史数据中。
这种方案解决了历史数据与时时数据的兼容问题。但也是有缺点的:
1:数据冗余,多存储了一份快照数据
2:需要更多的存储空间
3:随着历史数据的增加,检索速度会降低
4:由于数据冗余,对基础数据扩展时,还要对历史数据修改
5:维护性降低
6:完全不符合数据库范式

优点是:
1:解决了时时数据和历史数据的分离
2:时时数据在为归档的时候可以方便的变更
3:如果对历史表进行分割,或特定统计表,按条件查询时可能会比查询多个实体的效率高(因为不需要连表)
4:存储方案最简单


以上为本人卓见,这是我在以前的一个项目中用过的方法。但也觉得不是最优的方案,却也一直没有找到其它的替代方案。如果各位有什么高见的话,望指教,最好能写出一个完整点的描述。

领域模型都应该是当前实时活动的模型,为了跟踪过去资料,一般也会引入History模型,就象DDD中案例车辆跟踪系统Cargo一样,运输概念都是当前的,至于过去的运输情况都使用history模型,查询就涉及两种模型一起查。

所以,你的订单模型中,应该多一个history模型对象的引用。

感谢大家的热心详细回复,我也仔细看了每个朋友的回贴,说得都有一定的道理,我自己也在迷糊中摸索和思考,收益良多。

也说我表达不是很详细,不过大家也回答得很接近了,我主要想表达的是,对象关联的问题,在DDD书中,只有“聚合”的部份了写出了我最关心的问题,现在摘录一下,以加强我所表达的问题:

==============DDD中第89页引用=============
假设的我们在从数据库中删除一个Person对象。附带删除的还有这个人的名字、生日和职位描述。但是地址呢?其他人可能也住在同一个地址。如果把地址也删除掉,那么其它的Person对象将引用一个被删除的地址对象。但是,如果不把地址留下来,数据库中会积累一些无用的地址对象。自动垃圾收集机制可以消除这些元用的地址对象。但是,即使我们的数据库系统也提供这种机制,它终究还是一种技术上的措施,并没有决解根本的建模问题。
========================================
在书中只提出了问题,在后面的一两个示列中找了一点点问题的解决方案

============DDD第92页Purchase Order(采购单)的完整性========
这里只提供给了一些参考,他的意思就像我一样,复制出part(商品)这个价格到采购单项中

我的实际难点就是,对象关联问题,相关联的两个对象,一旦其中一个对象改变,另一端的关联对象如何保持变与不变的问题,比如像ddd说的,在数据库中删除了某一个对象,那么其它的对象将引用一个被删除的地址对象,如果用复制属性方式,我一个订单关联了好多个有可能会变的对象(比如,车辆会改[颜色、司机会更换等等,这个是不确定的,但是做我开发的我们应要允许吧],还有就是跟单员、客户对象的改变,这些对象都与订单关联,如果不个个都要像ddd中采购单的方式,那订单对像(Order)就像一个大杂烩了),虽然得到各位前辈的指点,但是这些问一直萦绕在头脑中,困住了

2010年10月08日 18:36 "freesea1"的内容
这些对象都与订单关联,如果不个个都要像DDD中采购单的方式,那订单对像(Order)就像一个大杂烩了), ...

这些对象的值会变,但是对象的类性质不会变,关联关系是类的关联,不是对象实例的关联,可能这方面有些混淆了。

根据变化的特点,切分不同变化,分别封装成单独的类。颜色属于商品规格,司机属于货运信息;跟单员属于订单操作者,这些都分别包装成独立的类即可。或者封装成值对象,值对象中值是一致变化的。
[该贴被banq于2010-10-09 17:26修改过]