领域驱动设计基本概念答疑

简介: 领域驱动设计基本概念答疑

实体与值对象  


问题:DDD实现中领域对象区分实体(Entity)和值对象(Value Object)的目的(Why)是什么?或者换一种问法:领域对象区分实体(Entity)和值对象(Value Object)之后,带来的好处和收益是什么?

回答:从DDD的概念上讲,实体(Entity)与值对象(Value Object)的本质区别仅在于后者无需identity(唯一标识)。这其实就是带来的价值——就是你设计的对象不需要去跟踪和管理这个唯一标识。

这是概念划分上,值对象带来的价值。

再来说设计层面。通常情况下,我们建议将值对象设计成一个不变(Immutable)对象。当一个对象是不变的时,你就基本不需要担心并发带来的诸如同步、冲突等问题了,这既降低了编程的难度,又可以无需引入额外的同步锁影响程序的性能。

反而过来说,之所以可以将值对象设计成不变的,其根本原因还是在于我们无需跟踪和管理唯一标识。

在领域驱动设计中,我们提倡的实践是尽量定义值对象来替代基本类型,原因在于基本类型无法体现统一语言中的领域概念。此外,在多数语言中,我们无法对基本类型做封装,就意味着一个领域概念缺乏领域行为来支持。假设一个实体定义了许多属性,如果这些属性都是基本类型,就会导致与这些属性相关的领域行为都要放到实体中,导致实体的职责变得不够单一。

引入值对象后,情况就不同了,因为我们可以利用合理的职责分配,将这些职责(领域行为)按照内聚性分配到各个值对象中,这个领域模型就能变得协作良好。

当然,反过来说,之所以可以这样设计,还是在于值对象无需承担跟踪和管理唯一标识的职责。

这也是为何Eric要将实体和值对象分开的主要原因,也是值对象给我们带来的价值所在。

Repository与DAO 


问题:Repository与DAO其实都是两种模式的名称。然而在领域驱动设计中,名称本身就是非常重要的。Dao即Data Access Object,即数据访问对象。从其命名上看,就应该属于数据访问层,即DDD中的基础设施层。

回答:在DDD中,所有的领域对象应该都属于领域层。那么,该如何访问这些领域对象呢?DDD希望解除领域层与基础设施层之间的关系,即将设计的注意力完全放在领域建模和领域设计上,思考领域逻辑的实现时,应尽可能地不要考虑领域对象的持久化(数据访问),于是就定义了Repository这个抽象。无论放在哪里(文件、DB或者内存),Repository都将其视为一个“资源库”的抽象。经过这么一层的抽象之后,获取领域对象,或者说管理领域对象生命周期的逻辑就应该属于领域层。

在实现上,你当然可以将这样的Repository接口命名为DAO,这本身没有问题,但名不正则言不顺,如果在领域层中夹杂了一个名为DAO的接口,仍然有“将基础设施混入领域层”的嫌疑。

所以,Repository是抽象,代表了对领域对象生命周期的管理,但并不等于是持久化,持久化只是Repository的其中一种实现。你可以假设一台服务器无比的强大,内存大且永远不会宕机,这时何须持久化呢?但无论怎么修改生命周期的具体管理方式,都不会影响到Repository的抽象。

领域服务与应用服务  


问题:应用服务与领域服务的区别在哪?

回答:从分层架构上,应用服务属于应用层,领域服务属于领域层。

从职责上看,应用服务只是一个门面(Facade),它具体并不做领域服务的活儿,也就是不提供领域实现,也就是不包含业务逻辑。之所以要引入应用服务,有两个原因:

  • 领域服务或其他领域对象的粒度太细(便于协作、扩展和重用),不利于客户端的调用,基于“最小知识原则”,还是让客户端少知道这些领域对象协作的知识为好。此时的应用服务更像是对领域对象的一种“编排”。
  • 在调用领域对象去完成一个用例时,不可避免地要牵涉到一些属于“横切关注点”的内容,如事务、异常处理、授权认证等。这些横切关注点从职责上看,不属于领域层,放在领域服务中可能会导致对领域逻辑的污染,这些职责就像砌砖墙时需要的水泥。水泥自身不提供砖头的职责,但没有水泥,墙就没法砌起来。


相关文章
|
消息中间件 Java Kafka
Springboot集成高低版本kafka
Springboot集成高低版本kafka
2183 0
|
3月前
|
人工智能 测试技术 API
让大模型真正为你工作:一文读懂RAG与微调的选择逻辑
本文深入解析RAG(开卷考试)与微调(封闭特训)两大私有知识注入技术:RAG实时更新、可追溯但依赖检索质量;微调风格统一、响应快但成本高、难迭代。结合实践案例与评估方法,重点推荐2024主流“混合架构”——RAG管“说什么”,微调管“怎么说”,兼顾准确性与规范性。
523 8
|
Linux C语言
Linux 中的文件锁定命令:flock、fcntl、lockfile、flockfile
Linux 中的文件锁定命令:flock、fcntl、lockfile、flockfile
759 0
|
6月前
|
XML Java 开发者
springboot自动装配的基本原理
Spring Boot自动装配基于“约定大于配置”理念,通过@SpringBootApplication、@EnableAutoConfiguration与spring.factories机制,结合条件注解实现智能Bean加载。它根据依赖自动配置组件,大幅简化开发。其核心是AutoConfigurationImportSelector筛选符合条件的配置类,实现按需装配。开发者可专注业务,享受“开箱即用”的便捷体验。(238字)
|
传感器 测试技术 atlas
Landsat系列卫星:Landsat 9 详解和细节(NASA/USGS)
Landsat系列卫星:Landsat 9 详解和细节(NASA/USGS)
4451 0
Landsat系列卫星:Landsat 9 详解和细节(NASA/USGS)
|
存储 监控 数据可视化
常见的分布式定时任务调度框架
分布式定时任务调度框架用于在分布式系统中管理和调度定时任务,确保任务按预定时间和频率执行。其核心概念包括Job(任务)、Trigger(触发器)、Executor(执行器)和Scheduler(调度器)。这类框架应具备任务管理、任务监控、良好的可扩展性和高可用性等功能。常用的Java生态中的分布式任务调度框架有Quartz Scheduler、ElasticJob和XXL-JOB。
5599 66
|
Java Maven 开发者
@EnableFeignClients:简化微服务间调用的艺术
@EnableFeignClients:简化微服务间调用的艺术
2508 2
|
JSON 自然语言处理 Java
这款轻量级 Java 表达式引擎,真不错!
AviatorScript 是一个高性能、轻量级的脚本语言,基于 JVM(包括 Android 平台)。它支持数字、字符串、正则表达式、布尔值等基本类型,以及所有 Java 运算符。主要特性包括函数式编程、大整数和高精度运算、完整的脚本语法、丰富的内置函数和自定义函数支持。适用于规则判断、公式计算、动态脚本控制等场景。
《跟老卫学仓颉编程语言开发》实战:猜数字游戏
本文介绍了用仓颉语言开发的一个简单猜数字游戏,综合运用了流程控制、标准输入、字符串操作和整型比较等知识。程序生成1到100之间的随机整数,用户通过标准输入猜数字,程序提示“太大”或“太小”,直至猜中并显示祝贺信息。代码使用`std.console`包处理输入,`std.convert`包实现字符串转整型,通过`if-else`判断大小,`while`循环支持多次输入。示例源于《跟老卫学仓颉编程语言开发》,源码可在其GitHub仓库获取。
647 0
|
设计模式 Java 测试技术
Spring状态机的实现原理和业务场景
**Spring State Machine**是Spring框架的一部分,它提供了一种实现状态机的方式,允许开发者定义状态机的状态、事件、行为和转换。状态机是一种计算模型,根据一系列规则从一个状态转移到另一个状态。【5月更文挑战第4天】
1024 2