跳至主要内容

ImplDDD 阅读笔记 - Value Objects, Part 2

在比较 Value Objects 的时候,首先比较的是两个对象的类型,其次才是对象的 attributes。如果两个或多个 value objects 是相等的,那么使用其中任意一个赋值给 Entity 的 property 都不会改变该 Entity Object 的状态。 A method of an object can be designed as a _Side-Effect-Free Function_. A function is an operation of an object that produces output but without modifying its own state. Since no modification occurs when executing a specific operation, that operation is said to be side-effect free. Value Object 的方法都是 Side-Effect-Free 的,因为一旦被创建出来就无法再修改 Value Object 的状态了。这个特性实际上跟 immutability 有很强的关联,不过作者认为有必要将其独立出来,这样可以更容易把 Value Object 当做一个可用的对象而非简单的一组 attributes 来使用。 FullName name = new FullName("Vaughn", "Vernon"); // later ... name = name.withMiddleInitial("L"); 这里调用的 `withMiddleInitial` 就可以是(也必须是)一个 function,它基于原有的 Value Object 创建了一个新的 Value Object。 public FullName withMiddleInitial(String aMiddleNameOrInitial) { if (StringUtils.isBlank(aMiddleNameOrInitial == null)) { throw new IllegalArgumentException("Must provide a middle name or initial."); } String middle = aMiddleNameOrInitial.trim(); return new FullName( this.firstName(), middle.substring(0, 1).toUpperCase(), this.lastName()); } 另外,作者还建议不要让 Value Object 接受 Entity Object 作为参数去修改其内部的状态值。例如,需要根据 Product 来判断优先级,`product` 是 Entity Object 而 `BusinessPriority` 则是 Value Object。 float priority = businessPriority.priorityOf(product); 这样做有几个坏处:首先,Value Object 应当只需要了解自己的结构,而不需要关心外部的 Entity 是什么情况;而且阅读代码的时候并不知晓 Value Object 到底使用了 Entity 内部的哪些信息,最最重要的是,任何一个这样的方法都使得 Value Object 难以对外解释(或证明自己)到底有没有对 Entity 做修改,即便 Value Object 郑重的承诺不会修改 Entity 可是外部用户很难做出判断。 可以将之前的表达式稍作修改,仅仅从 `product` 中提取处必要的信息,作为参数传入。这样很容易可以证明 `businessPriority` 上的方法没有对 `product` 做任何修改。 float priority = businessPriority.priorityOf( product.businessPriorityTotals()); 在不少系统和应用程序中都存在所谓的 lookup 或是 type code,作者建议使用术语 *Standard Types* 来描述这类概念。例如,当 Ubiquitious Language 需要描述 `PhoneNumber` (Value) 的时候,可能还需要描述电话号码的类型,是家里的电话,还是公司的电话,又或者是移动电话?很自然的,`Home`, `Mobile`, `Work` 甚至是 `Other` 都是 Standard Types。另一个例子是为了描述 `MonetaryValue`,可以定义 Standard Type `Currency` 来描述有限的几种货币,可以包括 AUD、CAD、CNY、EUR、GBP、JPY 和 USD。不可否认即便采用了这一标准类型,仍然可能会发生使用错误的货币种类的问题,但至少不会发生将根本不存在的货币种类用来描述 `MonetaryValue` 的错误。 或者,如果你在为制药领域 (pharmaceutical field) 设计领域模型,medication 的 administration routes 本身存在一个很长的周期,从最早的概念化 (conceptualized),进入研究阶段 (researched) 和开发阶段 (developed),进而开始临床测试 (tested) 和生产 (manufactured),最后是药监局(FDA approved)因而可以发售 (distributed)。这漫长的周期中药品可以使用一组 Bounded Contexts 来进行管理,不过最终当药品进入病人手中的时候 (the directed patient administration route) 可以采用 Standard Types 将其归类为 `IV`, `Oral` 或是 `Topical` 这几类。 > Depending on the level of standization, these types may be maintained at the application level only, > or be escalated in importance to shared corporate databases, or be available through national or > international standards bodies. The level of standardization can sometimes influence the way Standard > Types are retrieved and used inside a model. 在特定的与这些标准类型有关的 Bounded Context 内考虑这些类型的时候,不妨将其看做 Entities,因为它们有自己的生命周期。但是无论这些标准类型是如何被创建和维护的,当我们在 Consuming Context 内考虑它们的时候,应当尽可能视作 Values,因为它们被用于测量或是描述对象,只要等值它们应当可以被随意替换。 > Thus, if there is no need to maintain a continuity of change over the life cycle of descriptive > types in the Bounded Context, model them as Values. Value Objects 的保存有若干种方式,总的来说它涉及到将 Values 序列化为文本或是二进制格式再保存。比起如何保存个别的 Value instances,更让人感兴趣的是使用这些 Values 的 Aggregate instance 如何持久化它们的值。 > All of the following discussions are based on the assumption that an Aggregate is being added to > or read from its Repository, and its contained Values is being added to or read from behind the > scenes along with the Entity - such as the Aggregate Root - that contains them. 在建模的时候必须要坚持 Reject undue influence of Data Model Leakage。要尽可能的按照 domain model 来设计 data model,而不是反其道而行。因为后者意味着开发者在维护 persistence perspective,因而照这种方式建立的 domain model 只不过是 data model 的投影 (projection)。 不可否认,database referential integrity 在很多时候起到了作用 (例如 foreign key constraints);经常需要依赖数据库为 key columns 加上索引以提升查询效率;也存在某些情况需要让 business data 支持 BI reporting tools。但是所有的这些例子都存在一个前提——要放在合适而且是必要的地方。在做设计的时候,Data Model 必须是辅助 Domain Model 的。 > Most conclude that reporting and business intelligence should not operate against > your production data, and should instead have a dedicated, specially designed data model. > DDD is not about structing data in a normalized fashion, it's about modeling > Ubiquitious Language in a consistent Bounded Context.

评论