解构领域驱动设计(三):领域驱动设计

在上一部分,分层架构的目的是为了将业务规则剥离出来在单独的领域层中进行实现。再回顾一下领域驱动设计的分层中应用层代码的实现。数据库

@Override
public void pay(int orderId, float amount) {
    DesignerOrder order = designerOrderRepository.selectByKey(orderId);   // 领域对象的加载
    if (order == null) {
        AppException.throwAppException(AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST_CODE, AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST, orderId);
    }
    
    order.pay(amount);    // 领域对象业务规则实现
    designerOrderRepository.update(order);    // 领域对象状态持久化
}

 

全部的业务规则都抽象到领域对象,好比“order.pay(amount)”抽象了付款的业务规则。领域对象由状态(对象的字段、属性)和操做(对象的方法)构成,领域对象的操做用于实现业务规则,业务规则执行完成后更改领域对象的状态。领域对象的持久化交给了基础设施层,这里,Repository目的是持久化领域对象状态。编程

领域驱动设计,即领域模型驱动程序设计,它的核心是保证系统的实现与实际的业务规则一致,完整实现了领域模型。它包含了两个部分:领域模型、领域模型的编程实现。架构

在软件设计和实现过程当中要充分利用领域模型,设计过程当中,领域模型做为与业务专家的沟通语言;实现过程当中,领域模型做为与开发人员沟通的语言。领域模型在软件生命周期过程做为通用语言。app

1 领域模型ide

领域建模(这里不重点介绍如何建模)方法论产出领域模型。咱们可使用UML建模,使用最简单、最容易理解的名词-形容词-动词法对领域知识进行建模,使用该模型做为与业务、技术团队沟通的通用语言。ui

在名词-形容词-动词法建模方法中,领域知识中的名词通常对应模型、形容词对应模型属性、动词对应模型方法。模型之间的关系有:组合、聚合、关联、依赖,四者关系由强到弱。this

依赖(Dependency)关系是类与类之间的联接。依赖关系表示一个类依赖于另外一个类的定义。通常而言,依赖关系在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用。 编码

关联(Association)关系是类与类之间的联接,它使一个类知道另外一个类的属性和方法。关联能够是双向的,也能够是单向的。在Java语言中,关联关系通常使用成员变量来实现。 spa

聚合(Aggregation) 关系是关联关系的一种,是强的关联关系。聚合是总体和个体之间的关系。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便总体和个体的关系。与关联关系同样,聚合关系也是经过实例变量实现的。可是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个表明总体,另外一个表明部分。 设计

组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中表明总体的对象负责表明部分对象的生命周期,组合关系是不能共享的。表明总体的对象须要负责保持部分对象和存活,在一些状况下将负责表明部分的对象湮灭掉。表明总体的对象能够将表明部分的对象传递给另外一个对象,由后者负责此对象的生命周期。换言之,表明部分的对象在每个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和总体的生命周期同样。 

简而言之,组合关系表示部分与总体关系,部分不能单独存在;聚合关系表示稍弱的部分与总体关系,部分能够单独存在;关联关系是一个模型和另外一个模型的联接,好比一个订单有一个顾客而一个顾客有多个订单;依赖是最弱的关系,表示一个模型的实现使用到另外一个模型的功能。

举个例子,咱们与业务专家沟通,梳理了以下业务知识,而后咱们使用名词-形容词-动词法来进行建模。

=====================
领域知识:装修设计预定平台
1 客户经过系统预定设计师进行装修设计,客户只能预定一个设计师订单,不能预定多个同时进行设计。
2 预定后,设计师上门进行量房,根据面积进行报价和预估设计时间。设计师订单按照4个节点预估交付时间,在不一样节点交付不一样成果,这四个节点分别为平面图、效果图、施工
图、交底,四个节点的付款比率分别为10%、40%、40%、10%。
3 客户接受报价方案后,进行付款,设计师开始设计;若是拒绝,则设计师能够进行再次报价和预估设计时间。
4 客户在付款以前,均可以进行终止。
5 客户付款后,正式进入设计阶段。设计师按阶段推动设计并按阶段更新进度。在每个阶段,设计师完成任务后,客户进行阶段成果确认,客户肯定后全部阶段后,订单自动完成。
6 客户能够对完成的订单进行评价。
7 客户对已付款但未完成的订单能够提出退款申请,退款计算方法依据当前设计进度,若是当前进度已经达到设计师请求施工图设计确认进度或超过该进度,则不容许退款。若是容许退款,退款金额最多为(总额 - 已完成的各阶段付款之和),最少为未完成交付节点的待付款总额。
8 申请经过的退款订单再也不容许更新进度。
=====================

在这里咱们能够梳理出来的名词有:客户、设计师订单、设计师、订单交付进度与交付节点、退款订单。
和设计师订单有关的动词有:量房、报价、接受(拒绝)报价、取消、付款、确认进度、退款、评价等。
设计师订单有关的属性有:订单金额、支付金额、面积、取消缘由、评价、状态等。

所以,咱们经过使用名词-形容词-动词法构建的模型图以下所示。

这里,模型有:客户Customer,设计师Designer,设计师订单DesignerOrder,退款单RefundOrder,设计进度DesigningProgressReport,设计进度节点DesigningProgressNode。模型中组合关系为:设计进度DesigningProgressReport,设计进度节点DesigningProgressNode;其它模型之间的关系为关联关系。

这个模型就做为软件开发和维护过程的通用语言。接下来,咱们将介绍如何来实现领域模型。

2 领域模型实现

在上一节,咱们介绍了经过领域建模来构建了领域模型。接下来咱们要介绍如何实现模型驱动程序设计,即咱们如何经过代码来实现领域模型对应的业务逻辑。领域模型的实现代码在领域层,它完整实现了领域模型的内部结构和模型之间的关系。

领域模型的实现代码由如下几个部分构成:
• 领域模型关系的实现:组合、聚合、关联、依赖。
• 领域模型的实现:实体和值对象。
• 跨领域模型的业务规则的实现:领域服务。

2.1 领域模型关系的实现

聚合、组合、关联关系在实现上的表现基本上是一个类(或者类的标识)做为另外一个类的属性;而依赖关系则是一个类做为另外一个类在方法的实现上的参数、变量,为另外一个类提供功能实现。

下面咱们简单看一下如何经过编码来实现类关联关系,好比在模型上客户和设计师订单是关联关系,一个客户能够有多个设计师订单,可是每个设计师订单只能有一个客户和一个设计师而且最多只有一个退款订单。

(1)聚合、组合、关联
表如今一个类持有另外一个类的引用,引用能够是实例的引用或者标识的引用,具体实现为属性。这种关系是双向关系,为了简化编码,可能只须要一方持有另外一方的引用便可,这依赖于具体要实现的业务逻辑。以下代码实现了DesignerOrder对设计师、进度报告的关系。

public class DesignerOrder implements Entity<DesignerOrder> {
    private int id;
    private int designerId;

    private DesigningProgressReport progressReport;
    ……

    public Designer getDesigner() {
        return designerRepository.getDesignerById(this.designerId);
    }

    public DesigningProgressReport getProgressReport() {
        return this.progressReport; 
    }

    ……
}

 

(2)依赖
依赖表如今一个类的实现使用到另外一个类的功能,依赖的类可能做为方法的参数、方法局部变量或者静态引用等。以下代码体现了对DesignerOrderWorkflowService的功能依赖。

public class DesignerOrder implements Entity<DesignerOrder> {
    public void pay(float amount) {
        Assert.isTrue(amount > 0, "The amount must be bigger than 0.");

        if (!DesignerOrderWorkflowService.canChangeState(state, DesignerOrderState.PAID)) {
            BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE_CODE, DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE, this.id, this.state);
        }

        if (Math.abs(amount - this.expectedAmount) > 0.01) {
            BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_MATCHED_CODE, DomainExceptionMessage.PAYMENT_NOT_MATCHED, this.id, this.expectedAmount, amount);
        }

        this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.PAID);
        this.actualPaidAmount = amount;

        // 付款完成后,自动启动进度跟踪
        this.progressReport.startup();
    }
}

 

2.2 领域模型的实现

领域模型在实现上表现为两类:(1)实体(Entity):这个领域模型有特定的标识,可是其内部状态会随着一序列的事件(对应业务规则的执行)发生变化,咱们把这类模型的实现称为实体;(2)值对象(Value Object):这个领域模型由属性来定义,实例建立后不会发生变动,变动也意味着从新建立一个实例,咱们把这类模型的实现称为值对象。

(1)实体
在装修设计预定平台的领域模型里面,咱们很容易能够发现设计师订单就是一个实体,在建立后,每个设计师订单有一个惟一的订单号,后续有量房、报价、付款、退款等系列动做的发生,从而订单的内部状态(字段值)会发生变化,可是都表明的是同一个订单。每个实体的实现都有一个标识。以下所示,这里的id字段表示了订单的惟一标识,并实现了Entity接口,Entity接口sameIdentityAs方法,判断实体的Id是否相同。

实体的属性和操做,对应着模型的状态和状态的变动,他们与模型的定义使一致的。

@Data
@EqualsAndHashCode(of = {"id"})
public class DesignerOrder implements Entity<DesignerOrder> {
    private int id;
    private DesignerOrderState state;
    private int customerId;
    private int designerId;
    private float area;

    private float expectedAmount;
    private int estimatedDays;
    private DesigningProgressReport progressReport;

    private String abortCause;

    private float actualPaidAmount;

    private int feedbackStar;
    private String feedbackDescription;

    private Date createdTime;
    private Date updatedTime;

    @Override
    public boolean sameIdentityAs(DesignerOrder other) {
        return this.equals(other);
    }

    public void measure(float area) {
        Assert.isTrue(area > 0, "The area must be bigger than 0.");

        this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.MEASURED);
        this.area = area;
    }

    public void quote(float amount, int[] estimatedDaysList) {
        Assert.isTrue(amount > 0, "The price must be bigger than 0.");
        this.assertEstimatedDaysList(estimatedDaysList);

        this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.QUOTED);
        this.expectedAmount = amount;
        this.progressReport = DesigningProgressReportFactory.newReport(this, estimatedDaysList);
        this.estimatedDays = this.progressReport.getEstimatedCompletionDays();
    }

    private void assertEstimatedDaysList(int[] estimatedDaysList) {
        if (null == estimatedDaysList || estimatedDaysList.length != 4) {
            throw new IllegalArgumentException("The size of estimatedDaysList must be 4.");
        }

        for (int days : estimatedDaysList) {
            if (days <= 0) {
                throw new IllegalArgumentException("Each element of estimatedDaysList must be bigger than 0.");
            }
        }
    }

    public void pay(float amount) {
        Assert.isTrue(amount > 0, "The amount must be bigger than 0.");

        if (!DesignerOrderWorkflowService.canChangeState(state, DesignerOrderState.PAID)) {
            BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE_CODE, DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE, this.id, this.state);
        }

        if (Math.abs(amount - this.expectedAmount) > 0.01) {
            BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_MATCHED_CODE, DomainExceptionMessage.PAYMENT_NOT_MATCHED, this.id, this.expectedAmount, amount);
        }

        this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.PAID);
        this.actualPaidAmount = amount;

        // 付款完成后,自动启动进度跟踪
        this.progressReport.startup();
    }

    public RefundOrder refund(String cause) {
        this.assertCanRefund();

        this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.REFUND);

        return RefundOrderFactory.newRefundOrder(this, cause);
    }
}

 

DDD对于实体有一段重要描述:当一个对象由其标识而不是属性区分时,那么在模型中应该主要经过标识来肯定该对象的定义。使类定义变得简单,并集中关注生命周期的连续性和标识。定义一种区分每一个对象的方式,这种方式应该与其形式和历史无关。要格外注意那些须要经过属性来匹配对象的需求。在定义标识操做时,要确保这种操做做为每一个对象生成惟一的结果,这能够经过附加一个保证惟一性的符号来实现。这种定义标识的方法可能来自外部,也多是由系统建立的任意标识符,但它在模型中必须是惟一的标识。模型必须定义出“符合什么条件才算是相同的事务”。

(2)值对象
在货物运输系统中,当咱们为一个货物的运输执行一条路线以后,那么这条路线不能发生变动,咱们倾向于把路由线路看作一个值对象。以下图所示。对于值对象,经过属性值便可标识。

public class RouteSpecification extends AbstractSpecification<Itinerary> implements ValueObject<RouteSpecification> {

  private Location origin;
  private Location destination;
  private Date arrivalDeadline;

  public RouteSpecification(final Location origin, final Location destination, final Date arrivalDeadline) {
    Validate.notNull(origin, "Origin is required");
    Validate.notNull(destination, "Destination is required");
    Validate.notNull(arrivalDeadline, "Arrival deadline is required");
    Validate.isTrue(!origin.sameIdentityAs(destination), "Origin and destination can't be the same: " + origin);

    this.origin = origin;
    this.destination = destination;
    this.arrivalDeadline = (Date) arrivalDeadline.clone();
  }

  public Location origin() {
    return origin;
  }

  public Location destination() {
    return destination;
  }

  public Date arrivalDeadline() {
    return new Date(arrivalDeadline.getTime());
  }

  @Override
  public boolean isSatisfiedBy(final Itinerary itinerary) {
    return itinerary != null &&
           origin().sameIdentityAs(itinerary.initialDepartureLocation()) &&
           destination().sameIdentityAs(itinerary.finalArrivalLocation()) &&
           arrivalDeadline().after(itinerary.finalArrivalDate());
  }

  @Override
  public boolean sameValueAs(final RouteSpecification other) {
    return other != null && new EqualsBuilder().
      append(this.origin, other.origin).
      append(this.destination, other.destination).
      append(this.arrivalDeadline, other.arrivalDeadline).
      isEquals();
  }

  @Override
  public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    final RouteSpecification that = (RouteSpecification) o;

    return sameValueAs(that);
  }

  @Override
  public int hashCode() {
    return new HashCodeBuilder().
      append(this.origin).
      append(this.destination).
      append(this.arrivalDeadline).
      toHashCode();
  }
}

 

值对象(Value Object)所包含的属性应该行程一个概念总体。当咱们只关心一个模型元素的属性时,应该把它归类为Value Object。咱们应该使这个模型元素可以表示出其属性的意义,并为它提供相关功能。Value Object应该是不可变的。不要为它分配粉盒标识,并且不要把它设计成像Entity那么复杂。

2.3 跨领域模型的业务规则的实现

咱们使用领域服务来封装不属于领域模型或者领域模型公共的业务规则。领域服务的方法通常是静态的,而且不会更改内部状态。在装修设计预定平台里面,咱们使用状态机工做流服务实现订单状态流转,它能够在设计师订单和退款单中共用。在《领域驱动设计》里面有一个示例,展现了转帐服务的实现,转帐动做实现的是从一个帐户到另外一个帐户的资金流转,所以将转帐设计到领域服务TransferService里面。关于服务的描述是:当领域中的某个重要的过程或转换操做不属于实体或值对象的天然职责时,应该在模型中添加一个做为独立接口的操做,并将其声明为Service。定义接口时要使用模型语言,并确保操做名称是领域模型的术语。此外,应该将Service定义为无状态的。

如下是服务示例。

public class DesignerOrderWorkflowService {
    private DesignerOrderWorkflowService() { }

    private static Map<DesignerOrderState, DesignerOrderState[]> states = new HashMap<>();
    static {
        states.put(DesignerOrderState.NEW, new DesignerOrderState[]{ DesignerOrderState.MEASURED, DesignerOrderState.ABORTED });
        states.put(DesignerOrderState.MEASURED, new DesignerOrderState[]{ DesignerOrderState.QUOTED, DesignerOrderState.ABORTED });
        states.put(DesignerOrderState.QUOTED, new DesignerOrderState[]{ DesignerOrderState.ACCEPT_QUOTE, DesignerOrderState.REJECT_QUOTE, DesignerOrderState.ABORTED });
        states.put(DesignerOrderState.REJECT_QUOTE, new DesignerOrderState[]{ DesignerOrderState.QUOTED, DesignerOrderState.ABORTED });
        states.put(DesignerOrderState.ACCEPT_QUOTE, new DesignerOrderState[]{ DesignerOrderState.PAID, DesignerOrderState.ABORTED });
        states.put(DesignerOrderState.PAID, new DesignerOrderState[]{ DesignerOrderState.REFUND, DesignerOrderState.COMPLETION });
        states.put(DesignerOrderState.COMPLETION, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK });

        states.put(DesignerOrderState.ABORTED, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK });
        states.put(DesignerOrderState.REFUND, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK });
        states.put(DesignerOrderState.FEEDBACK, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK }); // 容许屡次评价
    }

    public static boolean canChangeState(DesignerOrderState state, DesignerOrderState nextState) {
        Assert.notNull(state, "The state can not be null.");
        Assert.notNull(nextState, "The nextState can not be null.");

        DesignerOrderState[] nextStates = states.get(state);
        for (DesignerOrderState possibleNextState : nextStates) {
            if (possibleNextState.equals(nextState)) {
                return true;
            }
        }

        return false;
    }

    public static boolean canAbort(DesignerOrder order) {
        return canChangeState(order.getState(), DesignerOrderState.ABORTED);
    }

    public static DesignerOrderState changeState(long orderId, DesignerOrderState state, DesignerOrderState nextState) {
        if (!canChangeState(state, nextState)) {
            BusinessException.throwException(DomainExceptionMessage.STATE_CHANGE_ILLEGAL_CODE, DomainExceptionMessage.STATE_CHANGE_ILLEGAL, orderId, state, nextState);
        }

        return nextState;
    }

    public static boolean isCompleted(DesignerOrder order) {
        return order.getState() == DesignerOrderState.ABORTED ||
                order.getState() == DesignerOrderState.REFUND ||
                order.getState() == DesignerOrderState.COMPLETION ||
                order.getState() == DesignerOrderState.FEEDBACK;
    }
}

 

3 领域模型生命周期管理

领域模型的建立会包含业务规则,咱们应该将这些业务规则封装起来,使建立过程对应用层透明,这里引入Factory来实现建立。此外,对于实体,发生一系列事件后,其内部状态发生了变动,这些状态变动须要持久化,以使得应用程序可以恢复实体状态。对于值对象,咱们可能也须要持久化相应的属性。这里,咱们引入Repository来实现持久化管理。对于一些关联很紧密的对象,好比采购订单和商品,他们须要共同的知足一个规则(好比采购订单里面的商品的总额不能超过采购订单的限额),若是多个用户同时变动采购订单或者其包含的商品,就须要引入很复杂的锁。为了使关联紧密的对象在整个生命周期都保持一致性,咱们引入了聚合Aggregate,经过它来实现一致性。

 

 

总结一句话:建立阶段——Factory用于封装实现领域对象建立的业务规则;建立、修改——Aggregate用于封装紧密关联领域对象在生命周期内的数据一致性;存储——Repository用于封装领域对象持久化的逻辑。

 

3.1 紧密关联的领域对象的一致性维护—Aggregate

首先,咱们先看一下为何要引入Aggregate。这里以采购订单为例子,采购员建立采购订单时须要指定限额,而后增长采购项目,所以可能存在两个采购员对同一个建立的采购订单进行操做,来更改订单。

以下所示,对于采购订单0012946,当前的商品金额为700,限额为1000。采购员A可能更改商品项1的数量为5,其总额为900,知足限额;采购员B可能更改商品项2的数量为3,其总额也为900,知足限额。

当采购员A、B同时提交更新后,采购订单的总额为1100,超过了1000元限额,破坏了业务规则。

在传统的方法,当咱们采用如下方式更新采购订单商品,就会出现刚才破坏业务规则的状况发生。

PurchaseOrder purchaseOrder = purchaseOrderBiz.getByKey(“0012946”);

List<PurchaseOrderItem> purchaseOrderItems = purchaseOrderItemBiz.getByOrderId(“0012946”);
changePurchaseOrderItems(purchaseOrderItems);
if (new PurchaseOrderApprovedLimitSpecify(purchaseOrderItems, purchaseOrder).isSatisfied()) {
    purchaseOrderItemBiz.updateBatch(purchaseOrderItems);
}

 

为了不发生采购订单限额的业务规则被破坏,对采购订单项的变动,须要对采购订单加排它锁。

在DDD里面,引入了聚合(Aggregate)来解决这个问题。Aggregate时一组相关对象的集合,做为数据修改的单元,在整个生命周期中知足固定的业务规则。每一个Aggregate都有一个根(root)和一个边界(boundary)。边界定义了Aggregate的内部都有什么,根则是Aggregate中所包含的一个特定Entity。在Aggregate中,根是惟一容许外部对象保持对它的引用的元素,而边界内部的对象则能够互相引用。基于聚合,咱们来实现一致的采购订单业务规则以下。

(1)应用层经过如下方式来更新聚合根里面的内容,这里必须知足一致性规则:对聚合内部实体的状态变动,只能经过聚合根来实现,经过聚合根来维持业务一致性。

PurchaseOrder order = purchaseOrderRepository.load(id);
order.addItem(…)/removeItem(…)/updateItem(…); // 注意:这里是重点,对聚合根内部的变动,只能经过聚合根,不能经过获取内部对象进行操做
purchaseOrderRepository.save(order);

 

(2)聚合根对内部实体的状态变动以下。

public class PurchaseOrder {
    private PurchaseOrderItemRepository orderItemRepository;
    private List<PurchaseOrderItem> orderItems;
    // ……
    public void addItem(int itemId, int count) {
        PurchaseOrderItem orderItem = PurchaseOrderItemFactory.create(this, itemId, count);
        orderItems.add(orderItem);
        if (!new PurchaseOrderApprovedLimitSpecification(this).isSatisfied()) {
            BusinessException.throwException(…);
            return;
        }
        
        orderItemRepository.save(orderItem);
        this.updateTimestamp();
    }
    // ……
}

 

聚合根定义的规则以下:
• 根Entity具备全局标识,它最终负责检查固定规则。
• 根Entity具备全局标识。边界内的Entity具备本地标识,这些标识只有在Aggregate内部才是惟一的。
• Aggregate外部的对象不能引用除根Entity以外的任何内部对象。根Entity能够把对内部Entity的引用传递给它们,但这些对象只能临时使用这些引用,而不能保持引用。根能够把一个Value Object的副班传递给另外一个对象,而没必要关心它发生什么变化,由于它只是一个Value,再也不与Aggregate有任何关联。
• 做为上一条规则的推论,只有Aggregate的根才能直接经过数据库查询获取。全部其余对象必须经过关联的遍历才能找到。
• Aggregate内部的对象能够保持对其余Aggregate根的引用。
• 删除操做必须一次删除Aggregate以内的全部对象。
• 当提交对Aggregate彬姐内部的任何对象的修改时,整个Aggregate中的全部固定规则都必须被知足。

3.2 领域模型的建立—Factory

当建立一个对象或建立整个Aggregate时,若是建立工做很负责,或者暴露了过多的内部结构,则可使用Factory进行封装。领域模型的建立也可能隐含了业务规则,Factory能够向应用层屏蔽业务规则。如下是一个设计师订单的Factory类。

public class DesignerOrderFactory {
    private DesignerOrderFactory() {}

    public static DesignerOrder createOrder(int customerId, int designerId) {
        DesignerOrder designerOrder = new DesignerOrder();
        designerOrder.setCustomerId(customerId);
        designerOrder.setDesignerId(designerId);
        designerOrder.setState(DesignerOrderState.NEW);

        return designerOrder;
    }
}

 

结论:应该将建立复杂对象的实例和聚合的职责转移给一个单独的对象,这个对象自己在领域模型中可能没有职责,但它还是领域设计的一部分。提供一个封装全部复杂装配操做的接口,并且这个接口应该不须要上层引用要被实例化的对象的具体类。在建立Aggregate时,要把它做为一个总体,并确保它知足固定规则。

3.3 领域模型的持久化—Repository

Repository的目的是实现领域对象的持久化,用于领域对象关联查询、重建、添加和删除。咱们只为那些确实须要直接访问的Aggregate提供Repository,将全部对象的存储和访问操做交给Repository。以下是一个实例。

@Repository
public class DesignerOrderRepositoryImpl implements DesignerOrderRepository {
    private static final String DESIGNER_ORDER_TABLE = "designer_order";

    @Autowired
    private DesignerOrderMapper designerOrderMapper;

    @Override
    public void create(DesignerOrder order) {
        if (designerOrderMapper.create(order) == 0) {
            TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.CREATE);
        }
    }

    @Override
    public DesignerOrder selectByKey(int id) {
        DesignerOrder order = designerOrderMapper.selectByKey(id);
        buildConnection(order);
        return order;
    }

    @Override
    public DesignerOrder selectOneBySpecification(DesignerOrder example) {
        DesignerOrder designerOrder = designerOrderMapper.selectOneBySpecification(example);
        buildConnection(designerOrder);
        return designerOrder;
    }

    @Override
    public List<DesignerOrder> selectBySpecification(DesignerOrder example) {
        List<DesignerOrder> designerOrders = designerOrderMapper.selectBySpecification(example);
        buildConnection(designerOrders);
        return designerOrders;
    }

    @Override
    public void update(DesignerOrder order) {
        if (designerOrderMapper.update(order) == 0) {
            TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.UPDATE);
        }
    }
}

 

4 结论

领域驱动设计的模式以下所示。

 

综上,领域层的实现由聚合构成,每个聚合一般包含了聚合根和领域模型实现、Service、工厂、Repository、领域异常等。

最终装修设计预定平台的领域模型以下所示。