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

打印 上一主题 下一主题

主题 837|帖子 837|积分 2511



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

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

  • 请假人填写请假单提交审批。
  • 根据请假人身份、请假类型和请假天数进行校验并确定审批规则。
  • 根据审批规则确定审批人,逐级提交上级审批,核批通过则完成审批,核批不通过则退回申请人。
请假微服务利用了许多DDD的计划思想和方法,如聚合、实体、值对象、范畴服务和仓储模式。
2、聚合中的对象

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

聚合根是聚合的入口,负责维护聚合内所有对象的同等性。对于请假聚合,LeaveApplication是聚合根。
聚合根示例代码:LeaveApplication.java
  1. public class LeaveApplication {
  2.     private String leaveId;
  3.     private String applicantId;
  4.     private String type;
  5.     private int days;
  6.     private String status;
  7.     public LeaveApplication(String leaveId, String applicantId, String type, int days) {
  8.         this.leaveId = leaveId;
  9.         this.applicantId = applicantId;
  10.         this.type = type;
  11.         this.days = days;
  12.         this.status = "PENDING";
  13.     }
  14.     public void approve() {
  15.         if (!"PENDING".equals(this.status)) {
  16.             throw new IllegalStateException("Leave application is not in a pending state");
  17.         }
  18.         this.status = "APPROVED";
  19.     }
  20.     public void reject() {
  21.         if (!"PENDING".equals(this.status)) {
  22.             throw new IllegalStateException("Leave application is not in a pending state");
  23.         }
  24.         this.status = "REJECTED";
  25.     }
  26.     // Getters and setters
  27. }
复制代码
2.2、实体

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

值对象是不可变的对象,只包含属性,用于描述范畴中的特征。在请假聚合中,可以定义LeaveType作为值对象,表现请假的类型。
值对象示例代码:LeaveType.java
  1. public class LeaveType {
  2.     private final String type;
  3.     public LeaveType(String type) {
  4.         this.type = type;
  5.     }
  6.     public String getType() {
  7.         return type;
  8.     }
  9.     @Override
  10.     public boolean equals(Object o) {
  11.         if (this == o) return true;
  12.         if (o == null || getClass() != o.getClass()) return false;
  13.         LeaveType leaveType = (LeaveType) o;
  14.         return Objects.equals(type, leaveType.type);
  15.     }
  16.     @Override
  17.     public int hashCode() {
  18.         return Objects.hash(type);
  19.     }
  20. }
复制代码
2.4、范畴服务

范畴服务封装焦点业务逻辑,实现必要多个实体协作的范畴逻辑。在请假聚合中,审批逻辑可以被封装到一个范畴服务中。
范畴服务示例代码:LeaveApprovalService.java
  1. public class LeaveApprovalService {
  2.     private final LeaveRepository leaveRepository;
  3.     public LeaveApprovalService(LeaveRepository leaveRepository) {
  4.         this.leaveRepository = leaveRepository;
  5.     }
  6.     public void approveLeave(String leaveId) {
  7.         LeaveApplication leaveApplication = leaveRepository.findById(leaveId);
  8.         if (leaveApplication == null) {
  9.             throw new IllegalArgumentException("Leave application not found");
  10.         }
  11.         leaveApplication.approve();
  12.         leaveRepository.save(leaveApplication);
  13.     }
  14.     public void rejectLeave(String leaveId) {
  15.         LeaveApplication leaveApplication = leaveRepository.findById(leaveId);
  16.         if (leaveApplication == null) {
  17.             throw new IllegalArgumentException("Leave application not found");
  18.         }
  19.         leaveApplication.reject();
  20.         leaveRepository.save(leaveApplication);
  21.     }
  22. }
复制代码
3、范畴事件

范畴事件是范畴驱动计划中解耦的重要手段,通过事件机制实现范畴对象之间的松耦合。
3.1、范畴事件基类

范畴事件基类定义了范畴事件的根本结构和属性。
范畴事件基类示例代码:DomainEvent.java
  1. public abstract class DomainEvent {
  2.     private final LocalDateTime occurredOn;
  3.     protected DomainEvent() {
  4.         this.occurredOn = LocalDateTime.now();
  5.     }
  6.     public LocalDateTime getOccurredOn() {
  7.         return occurredOn;
  8.     }
  9. }
复制代码
3.2、范畴事件实体

详细的范畴事件实体继承自范畴事件基类,表现详细的业务事件。
范畴事件实体示例代码:LeaveApprovedEvent.java
  1. public class LeaveApprovedEvent extends DomainEvent {
  2.     private final String leaveId;
  3.     private final String applicantId;
  4.     public LeaveApprovedEvent(String leaveId, String applicantId) {
  5.         this.leaveId = leaveId;
  6.         this.applicantId = applicantId;
  7.     }
  8.     public String getLeaveId() {
  9.         return leaveId;
  10.     }
  11.     public String getApplicantId() {
  12.         return applicantId;
  13.     }
  14. }
复制代码
3.3、范畴事件的执行逻辑

范畴事件的执行逻辑可以通过事件发布器和事件处理器实现。
事件发布器示例代码:EventPublisher.java
  1. public class EventPublisher {
  2.     private final List<EventSubscriber> subscribers = new ArrayList<>();
  3.     public void subscribe(EventSubscriber subscriber) {
  4.         subscribers.add(subscriber);
  5.     }
  6.     public void publish(DomainEvent event) {
  7.         for (EventSubscriber subscriber : subscribers) {
  8.             subscriber.handle(event);
  9.         }
  10.     }
  11. }
复制代码
事件处理器示例代码:LeaveApprovedEventHandler.java
  1. public class LeaveApprovedEventHandler implements EventSubscriber {
  2.     @Override
  3.     public void handle(DomainEvent event) {
  4.         if (event instanceof LeaveApprovedEvent) {
  5.             LeaveApprovedEvent leaveApprovedEvent = (LeaveApprovedEvent) event;
  6.             // 处理请假批准事件的逻辑
  7.         }
  8.     }
  9. }
复制代码
3.4、范畴事件数据持久化

为了保证事件的持久化,可以将事件存储到数据库中。
事件持久化示例代码:EventStore.java
  1. public class EventStore {
  2.     private final List<DomainEvent> events = new ArrayList<>();
  3.     public void save(DomainEvent event) {
  4.         events.add(event);
  5.     }
  6.     public List<DomainEvent> findAll() {
  7.         return new ArrayList<>(events);
  8.     }
  9. }
复制代码
4、仓储模式

仓储模式用于管理聚合的持久化和检索,通过依赖倒置原则实现底子资源和业务逻辑的解耦。
4.1、DO与PO对象的转换

在持久化过程中,必要将范畴对象(DO)转换为持久化对象(PO),以顺应不同的持久化需求。
DO与PO转换示例代码:LeaveMapper.java
  1. public class LeaveMapper {
  2.     public static LeaveApplication toDomain(LeavePO po) {
  3.         return new LeaveApplication(po.getLeaveId(), po.getApplicantId(), po.getType(), po.getDays());
  4.     }
  5.     public static LeavePO toPersistence(LeaveApplication domain) {
  6.         LeavePO po = new LeavePO();
  7.         po.setLeaveId(domain.getLeaveId());
  8.         po.setApplicantId(domain.getApplicantId());
  9.         po.setType(domain.getType());
  10.         po.setDays(domain.getDays());
  11.         return po;
  12.     }
  13. }
复制代码
4.2、仓储实现逻辑

仓储实现负责详细的持久化操作,可以利用JPA或其他ORM框架。
仓储实现示例代码:LeaveRepository.java
  1. public interface LeaveRepository {
  2.     LeaveApplication findById(String leaveId);
  3.     void save(LeaveApplication leaveApplication);
  4. }
  5. @Repository
  6. public class JpaLeaveRepository implements LeaveRepository {
  7.     @Autowired
  8.     private LeaveJpaRepository leaveJpaRepository;
  9.     @Override
  10.     public LeaveApplication findById(String leaveId) {
  11.         LeavePO po = leaveJpaRepository.findById(leaveId).orElse(null);
  12.         return po == null ? null : LeaveMapper.toDomain(po);
  13.     }
  14.     @Override
  15.     public void save(LeaveApplication leaveApplication) {
  16.         LeavePO po = LeaveMapper.toPersistence(leaveApplication);
  17.         leaveJpaRepository.save(po);
  18.     }
  19. }
  20. public interface LeaveJpaRepository extends JpaRepository<LeavePO, String> {}
复制代码
5、 工厂模式

工厂模式用于创建复杂的聚合根对象,封装其创建过程,确保对象的同等性。
工厂模式示例代码:LeaveFactory.java
  1. public class LeaveFactory {
  2.     public static LeaveApplication createLeave(String applicantId
  3. , String type, int days) {
  4.         String leaveId = UUID.randomUUID().toString();
  5.         return new LeaveApplication(leaveId, applicantId, type, days);
  6.     }
  7. }
复制代码
6、服务的组合与编排

服务的组合与编排通过应用服务实现,将范畴服务和底子服务组合起来,实现复杂的业务流程。
应用服务示例代码:LeaveApplicationService.java
  1. @Service
  2. public class LeaveApplicationService {
  3.     @Autowired
  4.     private LeaveRepository leaveRepository;
  5.     @Autowired
  6.     private LeaveApprovalService leaveApprovalService;
  7.     public void applyLeave(String applicantId, String type, int days) {
  8.         LeaveApplication leaveApplication = LeaveFactory.createLeave(applicantId, type, days);
  9.         leaveRepository.save(leaveApplication);
  10.     }
  11.     public void approveLeave(String leaveId) {
  12.         leaveApprovalService.approveLeave(leaveId);
  13.     }
  14.     public void rejectLeave(String leaveId) {
  15.         leaveApprovalService.rejectLeave(leaveId);
  16.     }
  17. }
复制代码
7、微服务拆分时的代码调整

在微服务架构演进过程中,必要对代码进行重构和调整,以顺应新的架构需求。
7.1、微服务拆分前的代码

在拆分前,所有功能都集中在一个单体应用中,代码耦合度高,难以维护和扩展。
  1. public class LeaveApplicationService {
  2.     // 包含所有业务逻辑和持久化操作
  3. }
复制代码
7.2、微服务拆分后的代码

在拆分后,功能模块化,代码解耦,每个微服务独立负责特定的业务范畴。
  1. public class LeaveApplicationService {
  2.     @Autowired
  3.     private LeaveRepository leaveRepository;
  4.     @Autowired
  5.     private LeaveApprovalService leaveApprovalService;
  6.     // 只负责业务逻辑,持久化操作由仓储负责
  7. }
复制代码
8、服务接口的提供

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

facade接口用于封装复杂的业务逻辑,对外提供统一的服务接口。
facade接口示例代码:LeaveFacade.java
  1. @RestController
  2. @RequestMapping("/leaves")
  3. public class LeaveFacade {
  4.     @Autowired
  5.     private LeaveApplicationService leaveApplicationService;
  6.     @PostMapping
  7.     public ResponseEntity<Void> applyLeave(@RequestBody LeaveDto leaveDto) {
  8.         leaveApplicationService.applyLeave(leaveDto.getApplicantId(), leaveDto.getType(), leaveDto.getDays());
  9.         return new ResponseEntity<>(HttpStatus.CREATED);
  10.     }
  11.     @PutMapping("/{leaveId}/approve")
  12.     public ResponseEntity<Void> approveLeave(@PathVariable String leaveId) {
  13.         leaveApplicationService.approveLeave(leaveId);
  14.         return new ResponseEntity<>(HttpStatus.OK);
  15.     }
  16.     @PutMapping("/{leaveId}/reject")
  17.     public ResponseEntity<Void> rejectLeave(@PathVariable String leaveId) {
  18.         leaveApplicationService.rejectLeave(leaveId);
  19.         return new ResponseEntity<>(HttpStatus.OK);
  20.     }
  21. }
复制代码
8.2、DTO数据组装

DTO用于数据传输,简化前端与后端的交互,避免范畴对象的直接暴露。
DTO示例代码:LeaveDto.java
  1. public class LeaveDto {
  2.     private String applicantId;
  3.     private String type;
  4.     private int days;
  5.     // Getters and setters
  6. }
复制代码
9、微服务解耦计谋小结

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

  • 聚合内解耦:通过聚合根管理聚合内对象的同等性,避免直接引用。
  • 聚合间解耦:通过事件总线实现聚合间的异步通信,避免直接依赖。
  • 服务解耦:通过范畴服务和应用服务分层实现业务逻辑和持久化操作的解耦。
  • 数据传输解耦:通过DTO和VO实现前后端的数据解耦。
本章小结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表