You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:26:00