DDD之实体与值对象区别

实体

当我们需要考虑一个对象的个性特性时,或者需要区分不同的对象时,我们引入实体这个领域概念,一个实体是一个唯一的东西。并且可以在相当长的一段时间内持续的变化,我们可以对实体进行多次的修改。但是尽管经历了多次的修改,实体始终拥有唯一一个身份标识。
实体生成唯一标识的几种方案:

  • 用于提供唯一标识
    让用户自己输入,这是一种非常简单的方案,但是也可能会变得很复杂,因为我们需要高质量的标识。大多数情况下标识是不允许修改。
  • 应用程序生成
    比如UUID,GUID
String id = java.util.UUID.randomUUID().toString();

或采用生成随机数加密的方式

SecureRandom randomGenerator = new SecureRandom();
  int randomNumber = randomGenerator.nextInt();
  String randomDigits = new Integer(randomNumber).toString();
  try {
      MessageDigest encryptor = MessageDigest.getInstance("SHA-1");
      byte[] idBytes = encryptor.digest(randomDigits.getBytes());
  } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
  }

缺点:
1)没有排序,无法保证趋势递增。
2)UUID往往是使用字符串存储,查询的效率比较低。
3)存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。
4)传输数据量大
5)不可读。
这里建议采用Twitter的snowflake算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。
另外我们可以通过Commons项目的Commons Id组件实现

  • 持久化机制生成唯一标识
    有些数据库不支持标识的提前生成,比如MYSQL,但是对于Oracle是支持采用序列值的方式提前生成一个可用的标识。

值对象

值对象通常是用来度量和描述事物。我们可以非常容易的对其进行创建,测试,使用,优化和维护,所以在建模时,我们尽量采用值对象来建模。
当你只关注某个对象属性时,该对象便可以是一个值对象,为其添加有意义的属性,并赋予它相应的行为。我们需要把值对象看做一个不可变的对象,所以不要给它任何身份标识。
当你考虑一个对象是否能够作为值对象时,你需要考量它是否有一下特征:

  • 它度量或者描述了领域中的一件东西
  • 它可以作为不变量
  • 它将不同的相关的属性组合成了一个概念整体
  • 当度量和描述改变时,可以用另外一个值对象予以替换
  • 它可以与其他值对象进行相等性比较
  • 它不会对协作对象造成副作用

有的时候值对象要以实体的身份进行持久化。换句话说,某个值对象实例会单独占据一张表中的一条记录,而该表也是为值对象设计的,换句话讲它甚至拥有主键列。
这个时候你需要从领域模型的角度考虑问题,而不是从持久化的角度考虑。考虑它是否满足值对象的特征。
在写代码时,值对象采用final修饰。我们最容易忽视的是值对象的无副作用属性,值对象的方法只是用于产生属性,并不会改变其属性。例如String类,它的所有改变自身属性的方法都是返回一个新的String对象实现。

区别

实体的唯一性和可变性的特性将其与值对象区别开来。
如果你无法区分你只需要回答下列问题:
1.当前所建模的概念是描述领域中的一个东西还是只是用于描述和度量其他东西呢?
2.如果该概念起描述作用,那么它是否满足先前所提到的值对象的几大特征?
3.将该概念建模成实体是不是因为它拥有唯一标识,我们关注的是对象实例的个体性,并且需要在其整个生命周期跟踪其变化。
如果你的回答是“描述,是的,不是”,那么你应该建模成值对象。

上一篇:《DDD之领域,子域,限界上下文》
下一篇:《DDD之如何合理设计一个聚合》

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DDD(领域驱动设计)是一种软件设计思想,其中包含了四个重要的概念:实体对象、聚合根和领域服务。 1. 实体(Entity) 实体是指具有唯一标识的对象,它具有生命周期,并且在系统中需要被跟踪和修改。实体通常是通过唯一标识来进行识别和区分的。例如,订单、用户、商品等都可以是实体。 例如,订单实体包含了订单号、下单时间、订单状态等属性,同时具有一些操作,比如修改订单状态、添加订单项等。 2. 对象(Value Object) 对象是指没有唯一标识的对象,它的属性可以改变,但是不会改变对象的身份。对象通常作为实体的属性存在,比如订单中的收货地址、商品的价格等。对象通常是不可变的,即创建后不可修改。 例如,收货地址对象包含了姓名、电话、地址等属性,它们的可以改变,但是这个地址本身并没有唯一标识。 3. 聚合根(Aggregate Root) 聚合根是指一组具有关联关系的实体对象的集合,其中一个实体作为聚合根,负责管理整个聚合。聚合根可以保证整个聚合的完整性和一致性。在聚合内,实体只能通过聚合根来进行访问和修改。 例如,订单聚合包含了订单实体、订单项实体以及收货地址对象,订单实体是聚合根,通过订单实体管理整个聚合。 4. 领域服务(Domain Service) 领域服务是指在领域模型中,不属于任何实体对象的操作,它们通常是跨实体的业务操作,或者是需要进行复杂计算的操作。 例如,计算订单中的总金额就是一个领域服务,它需要查询订单中的所有订单项,并计算每个订单项的金额,最后求和计算出订单的总金额。 总之,DDD中的实体对象、聚合根和领域服务是设计领域模型的重要概念,它们能够帮助我们构建具有高内聚、低耦合、易扩展的领域模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值