19、基于DDD的微服务代码详解

news2024/9/9 1:25:13

本章将深入探讨如何基于领域驱动设计(DDD)开发微服务代码,并提供具体的代码示例和详细解释。我们将基于第十八章中的请假案例进行讲解,确保每个细节都不放过。

1、项目背景

回顾第十八章中请假案例的需求和设计,我们已经拆分出两个微服务:请假服务和考勤服务。请假服务的核心业务流程如下:

  1. 请假人填写请假单提交审批。
  2. 根据请假人身份、请假类型和请假天数进行校验并确定审批规则。
  3. 根据审批规则确定审批人,逐级提交上级审批,核批通过则完成审批,核批不通过则退回申请人。

请假微服务使用了许多DDD的设计思想和方法,如聚合、实体、值对象、领域服务和仓储模式。

2、聚合中的对象

请假微服务包含三个聚合:请假(leave)、人员(person)和审批规则(rule)。我们将详细解释每个聚合中的对象及其职责。

2.1、聚合根

聚合根是聚合的入口,负责维护聚合内所有对象的一致性。对于请假聚合,LeaveApplication是聚合根。

聚合根示例代码:LeaveApplication.java

public class LeaveApplication {
    private String leaveId;
    private String applicantId;
    private String type;
    private int days;
    private String status;

    public LeaveApplication(String leaveId, String applicantId, String type, int days) {
        this.leaveId = leaveId;
        this.applicantId = applicantId;
        this.type = type;
        this.days = days;
        this.status = "PENDING";
    }

    public void approve() {
        if (!"PENDING".equals(this.status)) {
            throw new IllegalStateException("Leave application is not in a pending state");
        }
        this.status = "APPROVED";
    }

    public void reject() {
        if (!"PENDING".equals(this.status)) {
            throw new IllegalStateException("Leave application is not in a pending state");
        }
        this.status = "REJECTED";
    }

    // Getters and setters
}
2.2、实体

实体是具有唯一标识的对象,其生命周期和状态会发生变化。在请假聚合中,LeaveApplication本身也是一个实体,因为它具有唯一的leaveId

2.3、值对象

值对象是不可变的对象,只包含属性,用于描述领域中的特征。在请假聚合中,可以定义LeaveType作为值对象,表示请假的类型。

值对象示例代码:LeaveType.java

public class LeaveType {
    private final String type;

    public LeaveType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        LeaveType leaveType = (LeaveType) o;
        return Objects.equals(type, leaveType.type);
    }

    @Override
    public int hashCode() {
        return Objects.hash(type);
    }
}
2.4、领域服务

领域服务封装核心业务逻辑,实现需要多个实体协作的领域逻辑。在请假聚合中,审批逻辑可以被封装到一个领域服务中。

领域服务示例代码:LeaveApprovalService.java

public class LeaveApprovalService {
    private final LeaveRepository leaveRepository;

    public LeaveApprovalService(LeaveRepository leaveRepository) {
        this.leaveRepository = leaveRepository;
    }

    public void approveLeave(String leaveId) {
        LeaveApplication leaveApplication = leaveRepository.findById(leaveId);
        if (leaveApplication == null) {
            throw new IllegalArgumentException("Leave application not found");
        }
        leaveApplication.approve();
        leaveRepository.save(leaveApplication);
    }

    public void rejectLeave(String leaveId) {
        LeaveApplication leaveApplication = leaveRepository.findById(leaveId);
        if (leaveApplication == null) {
            throw new IllegalArgumentException("Leave application not found");
        }
        leaveApplication.reject();
        leaveRepository.save(leaveApplication);
    }
}
3、领域事件

领域事件是领域驱动设计中解耦的重要手段,通过事件机制实现领域对象之间的松耦合。

3.1、领域事件基类

领域事件基类定义了领域事件的基本结构和属性。

领域事件基类示例代码:DomainEvent.java

public abstract class DomainEvent {
    private final LocalDateTime occurredOn;

    protected DomainEvent() {
        this.occurredOn = LocalDateTime.now();
    }

    public LocalDateTime getOccurredOn() {
        return occurredOn;
    }
}
3.2、领域事件实体

具体的领域事件实体继承自领域事件基类,表示具体的业务事件。

领域事件实体示例代码:LeaveApprovedEvent.java

public class LeaveApprovedEvent extends DomainEvent {
    private final String leaveId;
    private final String applicantId;

    public LeaveApprovedEvent(String leaveId, String applicantId) {
        this.leaveId = leaveId;
        this.applicantId = applicantId;
    }

    public String getLeaveId() {
        return leaveId;
    }

    public String getApplicantId() {
        return applicantId;
    }
}
3.3、领域事件的执行逻辑

领域事件的执行逻辑可以通过事件发布器和事件处理器实现。

事件发布器示例代码:EventPublisher.java

public class EventPublisher {
    private final List<EventSubscriber> subscribers = new ArrayList<>();

    public void subscribe(EventSubscriber subscriber) {
        subscribers.add(subscriber);
    }

    public void publish(DomainEvent event) {
        for (EventSubscriber subscriber : subscribers) {
            subscriber.handle(event);
        }
    }
}

事件处理器示例代码:LeaveApprovedEventHandler.java

public class LeaveApprovedEventHandler implements EventSubscriber {
    @Override
    public void handle(DomainEvent event) {
        if (event instanceof LeaveApprovedEvent) {
            LeaveApprovedEvent leaveApprovedEvent = (LeaveApprovedEvent) event;
            // 处理请假批准事件的逻辑
        }
    }
}
3.4、领域事件数据持久化

为了保证事件的持久化,可以将事件存储到数据库中。

事件持久化示例代码:EventStore.java

public class EventStore {
    private final List<DomainEvent> events = new ArrayList<>();

    public void save(DomainEvent event) {
        events.add(event);
    }

    public List<DomainEvent> findAll() {
        return new ArrayList<>(events);
    }
}
4、仓储模式

仓储模式用于管理聚合的持久化和检索,通过依赖倒置原则实现基础资源和业务逻辑的解耦。

4.1、DO与PO对象的转换

在持久化过程中,需要将领域对象(DO)转换为持久化对象(PO),以适应不同的持久化需求。

DO与PO转换示例代码:LeaveMapper.java

public class LeaveMapper {
    public static LeaveApplication toDomain(LeavePO po) {
        return new LeaveApplication(po.getLeaveId(), po.getApplicantId(), po.getType(), po.getDays());
    }

    public static LeavePO toPersistence(LeaveApplication domain) {
        LeavePO po = new LeavePO();
        po.setLeaveId(domain.getLeaveId());
        po.setApplicantId(domain.getApplicantId());
        po.setType(domain.getType());
        po.setDays(domain.getDays());
        return po;
    }
}
4.2、仓储实现逻辑

仓储实现负责具体的持久化操作,可以使用JPA或其他ORM框架。

仓储实现示例代码:LeaveRepository.java

public interface LeaveRepository {
    LeaveApplication findById(String leaveId);
    void save(LeaveApplication leaveApplication);
}

@Repository
public class JpaLeaveRepository implements LeaveRepository {
    @Autowired
    private LeaveJpaRepository leaveJpaRepository;

    @Override
    public LeaveApplication findById(String leaveId) {
        LeavePO po = leaveJpaRepository.findById(leaveId).orElse(null);
        return po == null ? null : LeaveMapper.toDomain(po);
    }

    @Override
    public void save(LeaveApplication leaveApplication) {
        LeavePO po = LeaveMapper.toPersistence(leaveApplication);
        leaveJpaRepository.save(po);
    }
}

public interface LeaveJpaRepository extends JpaRepository<LeavePO, String> {}
5、 工厂模式

工厂模式用于创建复杂的聚合根对象,封装其创建过程,确保对象的一致性。

工厂模式示例代码:LeaveFactory.java

public class LeaveFactory {
    public static LeaveApplication createLeave(String applicantId

, String type, int days) {
        String leaveId = UUID.randomUUID().toString();
        return new LeaveApplication(leaveId, applicantId, type, days);
    }
}
6、服务的组合与编排

服务的组合与编排通过应用服务实现,将领域服务和基础服务组合起来,实现复杂的业务流程。

应用服务示例代码:LeaveApplicationService.java

@Service
public class LeaveApplicationService {
    @Autowired
    private LeaveRepository leaveRepository;
    @Autowired
    private LeaveApprovalService leaveApprovalService;

    public void applyLeave(String applicantId, String type, int days) {
        LeaveApplication leaveApplication = LeaveFactory.createLeave(applicantId, type, days);
        leaveRepository.save(leaveApplication);
    }

    public void approveLeave(String leaveId) {
        leaveApprovalService.approveLeave(leaveId);
    }

    public void rejectLeave(String leaveId) {
        leaveApprovalService.rejectLeave(leaveId);
    }
}
7、微服务拆分时的代码调整

在微服务架构演进过程中,需要对代码进行重构和调整,以适应新的架构需求。

7.1、微服务拆分前的代码

在拆分前,所有功能都集中在一个单体应用中,代码耦合度高,难以维护和扩展。

public class LeaveApplicationService {
    // 包含所有业务逻辑和持久化操作
}
7.2、微服务拆分后的代码

在拆分后,功能模块化,代码解耦,每个微服务独立负责特定的业务领域。

public class LeaveApplicationService {
    @Autowired
    private LeaveRepository leaveRepository;
    @Autowired
    private LeaveApprovalService leaveApprovalService;

    // 只负责业务逻辑,持久化操作由仓储负责
}
8、服务接口的提供

微服务对外提供接口,以便其他服务或前端应用进行调用。接口设计需要考虑到数据传输对象(DTO)和视图对象(VO)的转换。

8.1、facade接口

facade接口用于封装复杂的业务逻辑,对外提供统一的服务接口。

facade接口示例代码:LeaveFacade.java

@RestController
@RequestMapping("/leaves")
public class LeaveFacade {
    @Autowired
    private LeaveApplicationService leaveApplicationService;

    @PostMapping
    public ResponseEntity<Void> applyLeave(@RequestBody LeaveDto leaveDto) {
        leaveApplicationService.applyLeave(leaveDto.getApplicantId(), leaveDto.getType(), leaveDto.getDays());
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @PutMapping("/{leaveId}/approve")
    public ResponseEntity<Void> approveLeave(@PathVariable String leaveId) {
        leaveApplicationService.approveLeave(leaveId);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @PutMapping("/{leaveId}/reject")
    public ResponseEntity<Void> rejectLeave(@PathVariable String leaveId) {
        leaveApplicationService.rejectLeave(leaveId);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
8.2、DTO数据组装

DTO用于数据传输,简化前端与后端的交互,避免领域对象的直接暴露。

DTO示例代码:LeaveDto.java

public class LeaveDto {
    private String applicantId;
    private String type;
    private int days;

    // Getters and setters
}
9、微服务解耦策略小结

通过本章代码详解,我们了解了用DDD设计和开发的微服务代码具体实现,重点关注了聚合、实体、值对象、领域服务、领域事件、仓储模式和工厂模式等DDD概念的实现细节。我们还探讨了微服务拆分过程中的代码调整和服务接口的设计。

解耦策略总结

  1. 聚合内解耦:通过聚合根管理聚合内对象的一致性,避免直接引用。
  2. 聚合间解耦:通过事件总线实现聚合间的异步通信,避免直接依赖。
  3. 服务解耦:通过领域服务和应用服务分层实现业务逻辑和持久化操作的解耦。
  4. 数据传输解耦:通过DTO和VO实现前后端的数据解耦。

本章小结

本章详细介绍了基于DDD的微服务代码实现,包括聚合中的对象、领域事件、仓储模式、工厂模式、服务的组合与编排以及微服务拆分时的代码调整。通过这些具体的代码示例和解释,读者可以深入理解DDD在微服务开发中的应用,并在实际项目中灵活运用这些知识和技能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1959074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型LLM- 微调P-Tuning v1

P-tuning v1 一文小结 这篇文章介绍了一种名为P-Tuning的新方法&#xff0c;用于改善预训练语言模型&#xff08;PLMs&#xff09;在自然语言理解&#xff08;NLU&#xff09;任务中的性能和稳定性。P-Tuning通过将可训练的连续提示嵌入&#xff08;continuous prompt embeddi…

Javascript前端面试基础(八)

window.onload和$(document).ready区别 window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行$(document).ready()是DOM结构绘制完毕后就执行&#xff0c;不必等到加载完毕 window.onload 触发时机&#xff1a;window.onload 事件会在整个页面&#xf…

【案例】区分是平行眼还是交叉眼,以及平行眼学习方法

案例一&#xff1a; 交叉眼&#xff1a;看到凸出的“灌水”&#xff0c;即文字好像显示在屏幕前面。PS&#xff1a;看的时候眼睛是斗鸡眼&#xff0c;容易疲劳 平行眼&#xff1a;看到凹陷的“灌水”&#xff0c;即文字好像显示在屏幕后面。PS&#xff1a;看的时候眼睛是平视…

前端JavaScript处理小数精度问题(最佳实践)

前言&#xff1a; 针对于小数精度问题&#xff0c;本次我们主要推荐两种方式&#xff0c;一种是简单的函数封装&#xff0c;一种是使用第三方库big.js。 方法一&#xff1a; 自封装函数搭配parseFloat和toFixed解决小数精度问题&#xff0c;仅适用于解决一般性小数精度问题&…

Java面试八股之简述spring的自动装配

简述spring的自动装配 Spring框架的自动装配&#xff08;Autowiring&#xff09;是一种机制&#xff0c;它允许Spring IoC容器自动满足Bean的依赖关系&#xff0c;而无需显式指定依赖注入的方式。这极大地简化了配置&#xff0c;并有助于减少配置错误。 Spring支持多种自动装…

硅纪元视角 | 苹果AI训练数据大曝光,坚持用户隐私第一

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

鲁迅曾经说过?现在没有中间派!以后也没有!——早读(逆天打工人爬取热门微信文章解读)

今天用了AI的风格模方&#xff0c;鲁迅的&#xff0c;开头那一小段改写&#xff0c;大家觉得如何&#xff1f; 引言Python 代码第一篇 续上第二篇 十点读书 “新型不孝”正在蔓延&#xff0c;很多父母浑然不知&#xff0c;还逢人就炫耀子女有出息结尾 引言 最近 我发觉自己的作…

手摸手教你撕碎西门子S7通讯协议02--socket连接

1、S7协议通讯流程回顾 1&#xff09;建立Socket连接&#xff1a;进行TCP三次握手 这里是指要建立socket的tcp连接&#xff0c;是tcp连接而不是udp连接&#xff0c;tcp连接是可靠连接&#xff0c;tcp连接就是要有稳定的IP地址&#xff0c;它是通过字节方式进行通讯&#xff…

OpenSSL SSL_connect: Connection was reset in connection to github.com:443

OpenSSL SSL_connect: Connection was reset in connection to github.com:443 目录 OpenSSL SSL_connect: Connection was reset in connection to github.com:443 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&…

云计算 华为云服务

配置虚拟私有云 云平台架管理 跳板机配置 ansible 管理主机 dnf install -y ansible-core glibc-langpack-zh ssh-keygenchmod 0400 /root/.ssh/id_rsa ssh-copy-id -i /root/.ssh/id_rsa.pub 192.168.1.125 ansible --version 代理 Yum 仓库 dnf install -y nginx vim /et…

MySQL使用教程 最最最实用的零基础教程 直接从安装开始教!!!!

数据构成了我们日益数字化的社会基础。想象一下&#xff0c;从移动应用和银行系统到搜索引擎&#xff0c;再到如 ChatGPT 这样的先进人工智能聊天机器人&#xff0c;这些工具若没有数据支撑&#xff0c;将寸步难行。你有没有好奇过这些海量数据都存放在哪里呢&#xff1f;答案正…

虚拟现实辅助工程技术在电气安全培训中的变革力量

近年来&#xff0c;随着数字化进程的不断加快&#xff0c;虚拟现实辅助工程&#xff08;VAE&#xff09;技术在各行各业中的应用愈发广泛。在电气安全培训领域&#xff0c;虚拟现实辅助工程技术正逐渐成为一种具有变革力量的工具。这项技术的引入不仅为电气工作者提供了更为安全…

会计财务公司代理小程序源码系统 带手机端 完整的源代码包以及搭建部署教程

系统概述 会计财务公司代理小程序源码系统是一款专为会计财务公司量身定制的小程序解决方案。该系统结合了财务管理、代理记账、税务筹划等核心功能&#xff0c;旨在帮助企业提高工作效率&#xff0c;优化客户服务体验。通过这款小程序&#xff0c;企业可以为客户提供更加便捷…

数据库管理-第225期 Oracle DB 23.5新特性一览(20240730)

数据库管理225期 2024-07-30 数据库管理-第225期 Oracle DB 23.5新特性一览&#xff08;20240730&#xff09;1 二进制向量维度格式2 RAC上的复制HNSW向量索引3 JSON集合4 JSON_ID SQL函数5 优化的通过网络对NVMe设备的Oracle的原生访问6 DBCA支持PMEM存储7 DBCA支持标准版高可…

【EasyAi】一个开箱即用,每一个Java开发者都可以使用的人工智能AI框架

前言 EasyAi对于Java的用处&#xff0c;等同于在JavaWeb领域spring的意义一样——做一个开箱即用&#xff0c;让每一个开发者都可以使用EasyAi&#xff0c;来开发符合自己人工智能业务需求的小微模型&#xff0c;这就是它的使命&#xff01; EasyAi介绍 EasyAi无任何依赖&…

【无标题】web+http协议+nginx搭建+nginx反向代理(环境准备)

一&#xff0e;Web 为用户提供互联网上浏览信息的服务&#xff0c;web服务是动态的&#xff0c;可交互的。 1.安装httpd yum -y install httpd 2.启动 systemctl start httpd 3.关闭防火墙 systemctl stop firewalld [rootrs html]# echo "我手机号是" > …

一篇文章掌握Python爬虫的80%

转载&#xff1a;一篇文章掌握Python爬虫的80% Python爬虫 Python 爬虫技术在数据采集和信息获取中有着广泛的应用。本文将带你掌握Python爬虫的核心知识&#xff0c;帮助你迅速成为一名爬虫高手。以下内容将涵盖爬虫的基本概念、常用库、核心技术和实战案例。 一、Python 爬虫…

昇思 25 天学习打卡营第 21 天 | MindSpore CycleGAN图像风格迁移互换

1. 背景&#xff1a; 使用 MindSpore 学习神经网络&#xff0c;打卡第 21 天&#xff1b;主要内容也依据 mindspore 的学习记录。 2. CycleGAN 介绍&#xff1a; MindSpore 的 CycleGAN 的图像风格迁移互换 论文地址 论文中文翻译地址 主要流程&#xff1a; 我们有一个转换…

做前端4年了,才明白技术的本质不过是工具而已

四年前&#xff0c;我踏上了前端开发的道路&#xff0c;从HTML和CSS到JavaScript&#xff0c;从jQuery到React&#xff0c;每一步都走得踏实而坚定。随着经验的积累&#xff0c;技术的进步&#xff0c;我逐渐认识到&#xff0c;所谓的“技术”&#xff0c;无非是实现目标的一种…

[C++探索]初始化列表,static成员,友元函数,内部类,匿名对象

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到C探索系列 作为一个程序员你不能不掌握的知识 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成本搭建个人网站…