本章将深入探讨如何基于领域驱动设计(DDD)开发微服务代码,并提供具体的代码示例和详细解释。我们将基于第十八章中的请假案例进行讲解,确保每个细节都不放过。
回顾第十八章中请假案例的需求和设计,我们已经拆分出两个微服务:请假服务和考勤服务。请假服务的核心业务流程如下:
请假微服务使用了许多DDD的设计思想和方法,如聚合、实体、值对象、领域服务和仓储模式。
请假微服务包含三个聚合:请假(leave)、人员(person)和审批规则(rule)。我们将详细解释每个聚合中的对象及其职责。
聚合根是聚合的入口,负责维护聚合内所有对象的一致性。对于请假聚合,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 } 实体是具有唯一标识的对象,其生命周期和状态会发生变化。在请假聚合中,LeaveApplication本身也是一个实体,因为它具有唯一的leaveId。
值对象是不可变的对象,只包含属性,用于描述领域中的特征。在请假聚合中,可以定义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); } } 领域服务封装核心业务逻辑,实现需要多个实体协作的领域逻辑。在请假聚合中,审批逻辑可以被封装到一个领域服务中。
领域服务示例代码: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); } } 领域事件是领域驱动设计中解耦的重要手段,通过事件机制实现领域对象之间的松耦合。
领域事件基类定义了领域事件的基本结构和属性。
领域事件基类示例代码:DomainEvent.java
public abstract class DomainEvent { private final LocalDateTime occurredOn; protected DomainEvent() { this.occurredOn = LocalDateTime.now(); } public LocalDateTime getOccurredOn() { return occurredOn; } } 具体的领域事件实体继承自领域事件基类,表示具体的业务事件。
领域事件实体示例代码: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; } } 领域事件的执行逻辑可以通过事件发布器和事件处理器实现。
事件发布器示例代码:EventPublisher.java
public class EventPublisher { private final List 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; // 处理请假批准事件的逻辑 } } } 为了保证事件的持久化,可以将事件存储到数据库中。
事件持久化示例代码:EventStore.java
public class EventStore { private final List events = new ArrayList<>(); public void save(DomainEvent event) { events.add(event); } public List findAll() { return new ArrayList<>(events); } } 仓储模式用于管理聚合的持久化和检索,通过依赖倒置原则实现基础资源和业务逻辑的解耦。
在持久化过程中,需要将领域对象(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; } } 仓储实现负责具体的持久化操作,可以使用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 {} 工厂模式用于创建复杂的聚合根对象,封装其创建过程,确保对象的一致性。
工厂模式示例代码: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); } } 服务的组合与编排通过应用服务实现,将领域服务和基础服务组合起来,实现复杂的业务流程。
应用服务示例代码: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); } } 在微服务架构演进过程中,需要对代码进行重构和调整,以适应新的架构需求。
在拆分前,所有功能都集中在一个单体应用中,代码耦合度高,难以维护和扩展。
public class LeaveApplicationService { // 包含所有业务逻辑和持久化操作 } 在拆分后,功能模块化,代码解耦,每个微服务独立负责特定的业务领域。
public class LeaveApplicationService { @Autowired private LeaveRepository leaveRepository; @Autowired private LeaveApprovalService leaveApprovalService; // 只负责业务逻辑,持久化操作由仓储负责 } 微服务对外提供接口,以便其他服务或前端应用进行调用。接口设计需要考虑到数据传输对象(DTO)和视图对象(VO)的转换。
facade接口用于封装复杂的业务逻辑,对外提供统一的服务接口。
facade接口示例代码:LeaveFacade.java
@RestController @RequestMapping("/leaves") public class LeaveFacade { @Autowired private LeaveApplicationService leaveApplicationService; @PostMapping public ResponseEntity applyLeave(@RequestBody LeaveDto leaveDto) { leaveApplicationService.applyLeave(leaveDto.getApplicantId(), leaveDto.getType(), leaveDto.getDays()); return new ResponseEntity<>(HttpStatus.CREATED); } @PutMapping("/{leaveId}/approve") public ResponseEntity approveLeave(@PathVariable String leaveId) { leaveApplicationService.approveLeave(leaveId); return new ResponseEntity<>(HttpStatus.OK); } @PutMapping("/{leaveId}/reject") public ResponseEntity rejectLeave(@PathVariable String leaveId) { leaveApplicationService.rejectLeave(leaveId); return new ResponseEntity<>(HttpStatus.OK); } } DTO用于数据传输,简化前端与后端的交互,避免领域对象的直接暴露。
DTO示例代码:LeaveDto.java
public class LeaveDto { private String applicantId; private String type; private int days; // Getters and setters } 通过本章代码详解,我们了解了用DDD设计和开发的微服务代码具体实现,重点关注了聚合、实体、值对象、领域服务、领域事件、仓储模式和工厂模式等DDD概念的实现细节。我们还探讨了微服务拆分过程中的代码调整和服务接口的设计。
解耦策略总结:
本章详细介绍了基于DDD的微服务代码实现,包括聚合中的对象、领域事件、仓储模式、工厂模式、服务的组合与编排以及微服务拆分时的代码调整。通过这些具体的代码示例和解释,读者可以深入理解DDD在微服务开发中的应用,并在实际项目中灵活运用这些知识和技能。