zoukankan      html  css  js  c++  java
  • DDD基本元素

    Entity
    以Identity作为其基本定义的对象,其存在形式和内容可以发生很大变化,但区分不同Entity的唯一准则就是Id。
    Entity对象并不主要是由它们的属性来定义。它们体现了标识在时间上的延续性,经常要经历多种不同的形态。有时,一个对象与另一个对象有不同的属性,但它们却是相互匹配的;有时一个对象与其他对象有着相同的属性,但它必须能够跟那些对象区分开来。
    比如在某个系统中Person被辨别为Entity:两个人都叫张三,但他们是不同的Person;一个人小时候叫张三,但后来觉得这个名字不好,就改名为张三丰,这是一个Person的属性发生了变化。
    唯一Id的生成是一个需要注意的问题,比如用数据库自增的方式产生的Id,对于在分布式系统甚至是跨系统的实现来说都不是“唯一”的,这会引发很多问题。
     
    Value Object
    如果一个对象代表了领域的某种描述性特征,并且没有概念性的Id,我们就称之为ValueObject。
    设计值对象时需要对复制、共享和不变性做出选择,为了能够尽量利用共享带来的好处,同时避免它的缺陷,只在以下情况中使用共享:
    • 当存储空间和对象数量有严格限定时
    • 当通信开销不高时(例如在一个中心服务器上)
    • 当共享对象具有严格不变性时
    尽量将值对象设计成不可更改的,除非:
    • 值经常被改变
    • 对象的生成和删除开销很大
    • 替换(不是修改)会打破集群
    • 值没有太多的共享,或共享并没有提高集群性能等其他技术原因
     
    Service
    领域中的一些概念不能作为模型中的对象来处理,而是作为一种接口来提供操作,它独立于模型,没有像Entity和ValueObject那样封装状态。
    服务代表了一种行为而不是一个实体。一个优秀的服务具备3种特征:
    • 与领域概念相关的操作不是实体和值对象中固有的部分
    • 接口根据领域模型中的其他元素来定义
    • 操作是无状态的
    要区分Application、Domain和Infrastructure服务,以资金转账为例:
    Application层
    1. 读取输入(例如XML请求)
    2. 发送消息给领域服务,要求处理
    3. 监听确认消息
    4. 决定用Infrastructure的服务发送通知
    Domain层
    1. 必要的账户和分类账对象的相互作用,完成正确的提取和存入
    2. 确认转账结果
    Infrastructure层
    1. 由应用选择通知方法,发送电子邮件、信件或通过其他通信途径
     
    Aggregate
    一个聚合是一簇相关联的对象,出于数据变化的目的,我们将这些对象视为一个单元。每个聚合都有一个根和一个边界。边界定义了聚合中包含什么,根则是聚合中唯一允许被外部对象引用的元素,是单个特定的Entity。
    • 聚合根具有全局标识,并最终负责对不变量的检查
    • 聚合边界以外的任何对象除了可以引用根,不能持有任何对其内部对象的引用。根可以把其内部实体的引用传递给其他对象,但是它们只能临时使用这种引用,而不能持有这种引用。它还可以复制一个值对象的副本传给另一个对象。它并不关心这个副本会发生什么变化,因为那只是一个值,而且与聚合已经不再有任何关联。
    • 能通过数据库查询直接获得的对象只有聚合根,所有其他对象必须通过导航关联来访问。
    • 聚合内的对象可以持有其他聚合根
    • 删除操作必须一次性删除聚合边界内的所有对象
    • 当在聚合边界内发生的任何对象修改被提交时,整个聚合的所有不变量必须都被满足
    不变量(Invariant)是指无论何时数据发生变化都必须满足的一致性规则。不能指望涉及聚合的任何规则都是每时每刻被满足的,但是聚合内部的不变量必须在每次事务完成时被满足。
     
    Factory
    用于Entity以及Aggregate的创建,尤其是创建Aggregate的时候最好使用Factory。实现形式可以是Factory Method、Abstract Factory或者Builder。
     
    在以下情况下可以不用Factory而是直接用构造函数:
    • 要创建的类型不是层次结构的一部分,没有多态的情况。
    • 客户关心创建时的具体实现
    • 客户可以使用对象的所有属性,因此其构造函数没有嵌入对象创建的逻辑
    • 创建过程非常简单
    • 构造函数是一个原子操作,创建出来的对象满足所有不变量
     
    Repository
    仓储将某种类型的所有对象描述为一个概念性的集合,而且包含精细的查询能力。仓储可以加入和删除具有合适类型的对象,为我们提供了对聚合根从产生之初到生命周期结束期间的访问能力。一般除了保存、更新和删除操作以外,仓储还可以提供标准化的查询接口比如GetById()、具体条件查询接口如GetByName(string name)以及更通用的查询接口GetByCriteria(Criteria c),如果需要,仓储也可以提供“有多少符合条件的对象数”或“某数值的累加和”等统计操作接口。
    仓储具有很多优点:
    • 为客户提供了一个简单的模型,来获取持久对象并管理其生命周期
    • 把应用和领域设计从持久技术、数据库策略甚至多种数据来源解耦
    • 传达对象访问的设计决策
    • 可以很容易的被替换成Mock实现,以便在测试中使用
    实现时的关注点:
    • 返回抽象类型、基类型
    • 与客户代码解耦
    • 让客户端代码来控制事物
     
    Domain Event
    有时Entity需要与Service和Repository打交道,如果把这样的业务放入Service,则容易造成贫血的实体,如果把Service或Repository注入到Entity中又会使得Entity反过来依赖Service或Repository,非常丑陋。所以为了解决这一问题有人提出DomainEvent的概念。通过Event Observer或者Message Bus,将一个Entity与Service、Repository解耦。
     
     
    Module
    当系统复杂到一定程度后,需要对Domain进行模块划分,划分的依据是高内聚、低耦合。模块提供了两种分析模型的方法:以模块为单位考虑它的实现细节,或从全局分析模块之间的联系而不用关心细节。
  • 相关阅读:
    Android之打包签名
    on a null object reference 问题的解决办法
    Android Fragment使用小结及介绍
    Android开发重点难点:RelativeLayout(相对布局)详解
    Android的学习第六章(布局一LinearLayout)
    与adb相关的问题,比如掉线问题、Android Studio 提示Session 'app':Error Installing APK、找不到设备
    Android LitePal介绍与使用说明
    学习进度条-10
    《梦断代码》阅读笔记03
    用户模板和用户场景
  • 原文地址:https://www.cnblogs.com/jiangdaoli/p/2986507.html
Copyright © 2011-2022 走看看