我应该在DDD中强制执行实体和价值对象的概念多少?

时间:2013-08-11 18:55:36

标签: c# design-patterns architecture domain-driven-design

我正在使用DDD开发一个C#项目。我写了一些课。有些属于实体类别,其他属于价值对象类别。我的问题是,为了忠实于实体和价值对象的定义,我必须或者应该做多少工作?

实体:

  • 禁止使用公共构造函数。必须使用工厂和/或静态方法,因此保证对象的唯一性(在带有id的内存中)。
  • 运算符==,!=重载和Equals方法覆盖,因此比较基于除对象引用之外的其他内容;一个id

价值对象:

  • 允许使用公共构造函数。允许使用工厂,静态方法,并且可以实现某种缓存,以便(可选地)保证对象的唯一性(如字符串实习)。
  • 运算符==,!=重载和等于方法覆盖,因此比较基于两个对象的所有值。也可以使用Struct。
  • 不可变

这些可能性强于执行概念,但是,这些是强制性的吗?因为制作那些工厂,那些静态方法,重载和覆盖所有这些方法似乎是一个巨大的负担或少量工作,应用DDD概念。

我应该走多远?

2 个答案:

答案 0 :(得分:4)

我喜欢一种编程原则,它被称为KISS

最重要的是尽可能地去做,不要只是因为你已经听说过或遇到它们而做事并遵循指导方针。而是从基本功能开始,并根据需要不断添加内容。

例如,您谈到了工厂。我通常不使用它们,除非对象创建很复杂或者我想强制执行某些条件,或者当你谈到运算符重载以进行比较时,我不这样做,除非我需要在我的代码中进行实际比较或需要它。我可以应用的一件事是Immutability,而不仅仅因为它适用于Value Objects,而且因为它在多线程支持方面是一个很好的工具,因为在创建后不能修改不可变对象。 / p>

摘要:从简单开始,根据需要添加内容。

答案 1 :(得分:1)

开始简单并进化。

例如: 假设我有一个实体PendingOrder,我用它开始:

应用层:

@Transactional
@Override
public PendingOrder placeOrder(Address deliveryAddress, Date deliveryTime) {
    PendingOrder pendingOrder = new PendingOrder(
            pendingOrderRepository.nextTrackingId(), deliveryAddress,
            deliveryTime);     //by constructor
    pendingOrderRepository.store(pendingOrder);
    return pendingOrder;
}

稍后,当客户端需要验证是否有可用的餐厅用于交付信息时,我引入了PendingOrderFactory来强制执行一些域约束:

应用层:

@Transactional
@Override
public PendingOrder placeOrder(Address deliveryAddress, Date deliveryTime) {
    PendingOrder pendingOrder = pendingOrderFactory.placeOrderWith(
            deliveryAddress, deliveryTime);//refactor to factory
    pendingOrderRepository.store(pendingOrder);
    return pendingOrder;
}

域层:

public PendingOrder placeOrderWith(Address deliveryAddress,
        Date deliveryTime) {

    if (restaurantRepository.isAvailableFor(deliveryAddress, deliveryTime)) {
        return new PendingOrder(pendingOrderRepository.nextTrackingId(),
                deliveryAddress, deliveryTime);
    } else {
        throw new NoAvailableRestaurantException(deliveryAddress,
                deliveryTime);
    }

}
另一方面,可以使用一些代码生成工具。这是我们在java中使用的

@ToString(of = "trackingId") //print PendingOrder.trackingId
@EqualsAndHashCode(of = "trackingId")//compare trackingId when Equals
public class PendingOrder {// this is an entity

@ToString //print all fields
@EqualsAndHashCode //compare all fields
@NoArgsConstructor
// @NoArgsConstructor for frameworks only
public class Address {// this is a value object

然而,我们只在必要时添加它们,通常,当您编写测试报告explict错误时需要@ToString,当您使用模拟编写测试时需要@EqualsAndHashCode。

例如:pendingOrderFactory是一个模拟器,我们验证它是用给定的参数(deliveryAddress,deliveryTime)调用的,模拟框架用Equals检查它,当我们不满足expecation时,会显示一个报告。模拟框架调用ToString来指示哪个对象打破了expecation。

@Test
public void placesAPendingOrder() throws Exception {
    final PendingOrder pendingOrder = new PendingOrderFixture().build();
    final Address deliveryAddress = pendingOrder.getDeliveryAddress();
    final Date deliveryTime = pendingOrder.getDeliveryTime();

    context.checking(new Expectations() {
   {
            allowing(pendingOrderFactory).placeOrderWith(deliveryAddress,
                    deliveryTime);//need to implement equals
            will(returnValue(pendingOrder));

            oneOf(pendingOrderRepository).store(pendingOrder);//need to implement equals
        }
    });

    PendingOrder order = target.placeOrder(deliveryAddress, deliveryTime);

    assertThat(order, is(pendingOrder));
}

希望这对Java示例有帮助和抱歉'因为我是C#白痴:(