如何在代码中建模域实体?

时间:2018-05-10 11:06:30

标签: oop entity domain-driven-design value-objects

我熟悉DDD和实体的概念。

根据DDD,实体是一个基本上由其身份定义的对象。

说,在我的项目中,我发现Account是一个实体。所以在代码中,这将由具有标识符字段的类表示,类似于

class Account { 
  private Id id
  private AccountStatus status
  ...
}

由于这是一个实体,因此除了Id之外,其所有字段的生命周期都会发生变化。

我的问题是,在状态变化和对象相等方面对代码进行建模的最佳方法是什么。

  • 州变更

由于实体的状态随时间而变化,是否应将类建模为可变类,还是应该使用每次状态更改创建的新引用进行不可变?

  • 平等

由于Account仅基于其Id标识,因此equals方法是否应仅比较对象的标识符?考虑所有 OR 无字段会有什么潜在问题。

2 个答案:

答案 0 :(得分:1)

我建议你复习Clojure的时代时间模型。 Stuart Halloway的2010年演讲Perception and Action详细介绍了epochal time model

Identities are successions of immutable states

在某种意义上,实体是对不可变状态的可变引用

在面向对象的风格中,我们可能会做类似

的事情
Entity {
    MutableRef<State> mutableRef;

    void change (...) {
        State current = mutableRef.get()
        State next = someFunction(current, ...)
        mutableRef.set(next)
    }
}

(我们通常不会这样做,因为在OO的Java谱系中,通常的做法是操纵可变值。)

  

我的问题是,在代码中对此进行建模的最佳方法是什么?我看到两种方法,

     
      
  • 由于这是一个实体,从概念上讲,其所有字段都可以更改,因此模型应该支持仅在Id字段上定义的变异(setter)和相等。

  •   
  • 将此模型作为值对象,即不可变的最终字段,在所有字段和变异上定义的相等,创建并返回一个新对象。

  •   

如果您使用业务语言将帐户描述为随时​​间变化的事物,则可能需要使用实体模型。

这并不一定意味着&#34; setters&#34 ;;更常见的风格是,mutators应该用业务语言编写。我们告诉实体该做什么

account.close()

及其实体的工作,以了解该操作如何影响基础数据结构。

至于身份,这是Evans最近的一句话:

  

我开始相信一个实体甚至不应该有一个相等的操作

视频片段似乎取自Julie Lerman和Steve Smith的2014 Pluralsight课程Domain-Driven Design Fundamentals模块。

答案 1 :(得分:0)

通常,Entities和ValueObjects是给定案例的互斥概念。不同的域也对不同的概念有不同的关注,其中一个概念可能是给定域中的ValueObject和另一个给定域中的实体。

一个这样的例子是Money作为一个概念。对于电子商务上下文,Money可能是一个ValueObject,因为域只关心它的值而不是它的持久身份(对于这样的系统,2个10美元的实例将是相同的,你不会关心它们分开) 。

对于联邦银行(或任何打印货币的实体),以完全不同的方式对待货币。这个域实际上用序列号标记单个账单,并且非常关心每个账单的历史记录(何时打印,这是模型,可能也是打印的位置)。因此,该域名可能会将Money建模为实体。

这些差异也会影响平等处理方式。

对于ValueObject,通常在“具有相同值的2个实例”上定义相等性,因此,您可能更好地匹配每个值字段以建立相等性。 Money实例“10 USD”等于另一个Money实例“10 USD”,但它与Money实例“10 EUR”不同(他们的FaceValue属性是相同的,但他们的货币不是这样你不能使用它们互换)。

现在,对于一个实体,您关心给定实例的整个生命周期的身份(作为一个概念,而不是实例化)。因此,如果您有一个序列号为1234且属性为“10 / USD / NearMint”的Money实例并且它已损坏,则会更改并保留其序列号1234,但其属性将更新为“10 / USD / Stained”。它仍然是相同的法案,并且对于所有帐户,它应该对平等比较器作出回应。

只是为了完成这个长答案,“setter”部分也依赖于域。通常,ValueObjects不应更改其内部状态。但实体应该只根据域规则更改其内部状态。在钱的例子中,即使它是一个实体,也许完全没有理由能够获得给定账单的FaceValue或Currency。然而,它的状态NearMint - &gt;染色 - &gt;损坏 - &gt;等等可能会随着时间的推移而发展。此外,您应该考虑不使用始终直接的setter,而是在实体中创建域有意义的方法,以便在处理状态转换时更好地表达无处不在的语言。