DDD预约场景中聚合根通信及预约流程实现方案问询
DDD预约系统问题解答
问题1:遵循DDD原则传递Doctor周排班至Schedule聚合根
在DDD中,聚合根是独立的一致性边界,应避免跨聚合根直接访问内部状态或依赖对方的Repository。正确的做法是通过应用服务作为协调者,利用Doctor聚合根暴露的业务方法完成排班校验,而非直接传递排班集合给Schedule,这样既保证了聚合根的封装性,又符合单一职责原则。
优化后代码示例
首先完善Doctor聚合根的业务方法,封装排班校验逻辑:
public class Doctor : AggregateRoot { public string FirstName { get; } public string LastName { get; } public DoctorType DoctorType { get; set; } // 私有set保证内部状态不被外部篡改 public List<WeeklySchedule> WeeklySchedules { get; private set; } = new List<WeeklySchedule>(); protected Doctor() {} public Doctor(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } // 暴露业务校验方法,而非直接返回排班集合 public bool IsAppointmentTimeValid(DateTime appointmentDateTime, TimeSpan duration) { var targetDay = appointmentDateTime.DayOfWeek; var dailySchedule = WeeklySchedules.FirstOrDefault(s => s.DayOfWeek == targetDay); if (dailySchedule == null) return false; var appointmentEndTime = appointmentDateTime.TimeOfDay.Add(duration); return appointmentDateTime.TimeOfDay >= dailySchedule.StartTime && appointmentEndTime <= dailySchedule.EndTime; } }
调整Schedule聚合根的SetAppointment方法,接收校验结果和必要参数,专注于预约创建的内部规则:
public class Schedule : AggregateRoot { private Schedule() {} private readonly List<Appointment> _appointments = new List<Appointment>(); public IEnumerable<Appointment> Appointments => _appointments.AsReadOnly(); public long DoctorId { get; set; } public long PatientId { get; set; } public Schedule(long doctorId, long patientId) { DoctorId = doctorId; PatientId = patientId; } public bool SetAppointment(DateTime appointmentDateTime, TimeSpan duration, bool isTimeValid) { if (!isTimeValid) return false; // 校验当前Schedule内是否存在预约冲突 var hasConflict = _appointments.Any(a => a.StartTime < appointmentDateTime.Add(duration) && a.EndTime > appointmentDateTime); if (hasConflict) return false; _appointments.Add(new Appointment { StartTime = appointmentDateTime, EndTime = appointmentDateTime.Add(duration), DoctorId = DoctorId, PatientId = PatientId }); return true; } }
问题2:预约创建流程的职责划分
根据DDD职责边界规则:
- 应用服务负责流程编排,协调聚合根、Repository的调用,处理跨聚合根的逻辑;
- 聚合根负责自身的业务规则校验和状态变更。
步骤职责分配
a. 获取患者信息:应用服务负责从Patient Repository查询实例,验证患者合法性;
b. 校验预约日期符合医生排班:由Doctor聚合根的业务方法完成(Doctor是排班规则的拥有者);
c. 创建预约:由Schedule聚合根完成(Schedule是预约的一致性边界,负责维护预约集合的完整性)。
完整代码示例
Appointment实体补充
public class Appointment : Entity { public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public long DoctorId { get; set; } public long PatientId { get; set; } }
应用服务实现
public class AppointmentAppService { private readonly IDoctorRepository _doctorRepository; private readonly IPatientRepository _patientRepository; private readonly IScheduleRepository _scheduleRepository; public AppointmentAppService(IDoctorRepository doctorRepo, IPatientRepository patientRepo, IScheduleRepository scheduleRepo) { _doctorRepository = doctorRepo; _patientRepository = patientRepo; _scheduleRepository = scheduleRepo; } public bool CreateAppointment(long doctorId, long patientId, DateTime appointmentDateTime, TimeSpan duration) { // a. 应用服务获取并验证患者信息 var patient = _patientRepository.GetById(patientId); if (patient == null) throw new ArgumentException("指定患者不存在"); // 应用服务获取医生信息 var doctor = _doctorRepository.GetById(doctorId); if (doctor == null) throw new ArgumentException("指定医生不存在"); // b. 调用Doctor聚合根的校验方法 var isTimeValid = doctor.IsAppointmentTimeValid(appointmentDateTime, duration); if (!isTimeValid) throw new InvalidOperationException("预约时间不在医生排班范围内"); // 获取或创建医患对应的Schedule聚合根 var schedule = _scheduleRepository.GetByDoctorAndPatient(doctorId, patientId) ?? new Schedule(doctorId, patientId); // c. 调用Schedule聚合根创建预约 var result = schedule.SetAppointment(appointmentDateTime, duration, isTimeValid); if (result) { _scheduleRepository.Save(schedule); } return result; } }
内容的提问来源于stack exchange,提问作者j jj
相关产品推荐
相关产品推荐

