3

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

实体:

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

值对象:

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

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

我应该走多远?

4

2 回答 2

4

有一个我喜欢的编程原则,它叫做KISS

底线是尽你所能,不要仅仅因为你听说过或遇到过就去做事情并遵循指导方针。而是从基本功能开始,并根据需要不断添加内容。

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

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

于 2013-08-11T22:12:40.243 回答
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

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

例如:pendingOrderFactory 是一个 mock,我们验证它是使用给定参数(deliveryAddress,deliveryTime)调用的,mock 框架通过 Equals 进行检查,当我们未能满足预期时,会显示一个报告。模拟框架调用 ToString 来指示哪个对象打破了预期。

@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# 白痴 :(

于 2013-08-12T01:41:42.633 回答