DDD案例(2):从领域分析到代码实现(8)

简介: DDD案例(2):从领域分析到代码实现(8)

梳理之后的领域设计类图非常规范。除了合成关系,存在OO聚合关系的实体都分到不同的聚合中,更不用说完全独立的Backlist实体。如果多个聚合边界的实体依赖了相同的值对象,可以定义多个相同的值对象,然后将它们放到各自的聚合边界内。分解关系薄弱处确定的聚合边界如图20-55所示。


image.png


考虑聚合设计原则,由于Learning聚合中的Course实体具有独立性,因此需要对图20-55稍做调整,将Course实体分离出来,定义为单独的聚合。除此之外,其余聚合边界都是合理的,不需再做调整。最终,确定了聚合边界的领域设计类图如图20-56所示。

由此得到的聚合包括:

qTraining聚合;

qCourse聚合;

qLearning聚合;

qTicket聚合;

qTicketHistory聚合;

qFilter聚合;

qValidDate聚合;

qValidDateAction聚合;

qCancellingAction聚合;

qCandidate聚合;

qAttendance聚合;

qBlacklist聚合。


image.png


即使在领域设计模型中,我们也无须为领域模型对象定义字段。每个聚合内的实体或值对象到底需要定义哪些字段,可以结合业务服务,通过测试驱动开发逐步驱动出来。领域设计类图最重要的要素是聚合。一旦确定了聚合,实际上也就确定了管理聚合生命周期的资源库。至于需要哪些领域服务和其他角色构造型,可以交由服务驱动设计来识别。

2)服务驱动设计

驱动设计的起点是业务服务。以提名候选人业务服务为例,将业务服务规约的基本流程转换为由动词短语组成的任务,然后通过向上归纳和向下分解获得由组合任务与原子任务组成的任务树:

q提名候选人(业务服务)

♦   确定候选人是否已经参加过该课程

○   获取该培训对应的课程

○   确定课程学习记录是否有该候选人

♦   如果未参加,则提名候选人

○   获得培训票

○   提名

○   保存票的状态

♦   发送提名通知

○   获取通知邮件模板

○   组装提名通知内容

○   发送通知

结合任务分解与角色构造型,它的序列图脚本如下:

 

NominationAppService.nominate(nominationRequest) {

  LearningService.beLearned(candidateId, trainingId) {

     TrainingRepository.trainingOf(trainingId);

     LearningRepository.isExist(candidateId, courseId);

  }

  TicketService.nominate(ticketId, candidate) {

     TicketRepository.ticketOf(ticketId);

     Ticket.nominate(candidate);

     TicketRepository.update(ticket);

  }

  NotificationService.notifyNominee(ticket, nominee) {

     MailTemplateRepository.templateOf(templateType);

     MailTemplate.compose(ticket, nominee);

     NotificationClient.notify(notificationRequest);

  }

}

 

该序列图脚本对应的序列图如图20-57所示。

20-57中的NominationAppService应用服务承担了多个领域服务之间的协作职责,且需要根据beAttend()方法的返回结果决定提名的执行流程。这实际上属于领域逻辑的一部分,故而应该在NominationAppService应用服务内部引入一个领域服务来封装这些业务逻辑。新增的领域服务为NominationService修改后的序列图如图20-58所示。


image.png

image.png

image.png

image.pngimage.png


20-58中的MailTemplate是一个聚合,存储了不同类型操作需要通知的邮件模板。在前面的领域分析建模与领域设计建模时,未能发现该聚合。这也印证了领域建模很难一蹴而就,需要不断地迭代更新和演进。

相关文章
|
消息中间件 NoSQL Java
springboot redis 实现消息队列
springboot redis 实现消息队列
1060 1
|
Java
《Java并发库系列三》一newSingleThreadScheduledExecutor
newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,如果任务多于一个,任务将按先后顺序执行。
1455 0
|
Arthas SQL Java
Arthas之WatchSql
在使用Arthas排查线上问题的时候,有些时候我们需要查看某些Sql的生成,如果线上没有完备的APM的话,那么如何临时查看呢,前几篇文章我们分析了Mybatis的插件机制,如果你还记得的话,我们可以通过watch这个插件进行查看。
3261 1
Arthas之WatchSql
|
7月前
|
Web App开发 Linux Shell
CuteHTTPFileServer下载,局域网文件传输工具下载,chfs支持的最低SSL版本为SSLv3
FinalShell是一款支持多平台的SSH客户端工具,提供一体化服务器管理功能,支持shell与sftp同屏显示、命令自动提示和访问加速,操作简单高效。
643 12
|
6月前
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
1225 0
|
负载均衡 网络协议 算法
一文读懂什么是Nginx?它能否实现IM的负载均衡?
Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡方案,从某种意义上来讲,Nginx几乎是低成本、高负载Web服务端代名词。 如此深入人心的Nginx,很多人也想当然的认为,在IM或消息推送等场景下是否也能使用Nginx来解决负载均衡问题? 另外,即时通讯网的论坛和QQ群里也经常有人问起,Nginx是否能支持TCP、UDP、WebSocket的负载
321 4
|
8月前
|
网络协议 Java
在SpringBoot项目中使用Netty实现远程调用
本文介绍了使用Netty解决网络连接性能问题的方法,重点讲解了Netty的NIO特性及其在SpringBoot中的应用。Netty作为高效的NIO框架,支持非阻塞IO,能通过单线程管理多个客户端连接,简化TCP/UDP套接字服务器开发。文章详细展示了Netty在SpringBoot中实现远程调用的过程,包括服务端与客户端代码实现、依赖配置及测试验证。通过示例代码,如`NettyServer`、`NettyClientUtil`等,清晰说明了Netty的工作原理和实际应用,解决了半包等问题,并提供了完整的测试结果。
884 3
|
存储 设计模式 数据可视化
DDD新手入门:领域模型设计的七个核心概念
小米,29岁程序员,分享领域模型落地知识。文章解析领域、子域、限界上下文、领域对象、聚合、工厂与仓库等概念,助你理解领域驱动设计。
933 1
|
消息中间件 存储 NoSQL
物联网设备频繁断网,如何打赢智慧社区的流量洪峰之战?
本文详细介绍了智慧社区中物联网(IOT)技术的应用,重点讨论了物联网流量洪峰的处理方法。文章分析了上行和下行消息的特点,并提出了上下行拆分、多泳道消息队列、实时消息优先处理、连接计算存储分离及推拉结合的消息策略,以优化消息队列,确保系统稳定运行。通过这些技术手段,智慧社区的物联网设备能在各种场景中保持高效运作。
273 2
|
数据采集 缓存 IDE
Go中遇到http code 206和302的获取数据的解决方案
文章提供了解决Go语言中处理HTTP状态码206(部分内容)和302(重定向)的方案,包括如何获取部分数据和真实请求地址的方法,以便程序员能快速完成工作,享受七夕时光。
1195 0
Go中遇到http code 206和302的获取数据的解决方案