【问题标题】:Domain Driven Design and Working with polymorphic Child Entities领域驱动设计和使用多态子实体
【发布时间】:2015-08-04 16:33:45
【问题描述】:

我一直在考虑处理具有多态子的聚合实体的最佳方法,想知道是否有人可以告诉我下面的实现是否是正确的实现,或者我是否可能忽略了一些潜在的陷阱。

以下代码演示了一个系统,用于按不同的时间表发送某种通信。

领域

class Delivery {
    Dictionary<Type, Func<ScheduleDto, ScheduleEntity>> ScheduleFactories = 
        new Dictionary<Type, Func<ScheduleDto, ScheduleEntity>> {
            { typeof(DailyScheduleDto), GetDailySchedule},
            { typeof(HourlyScheduleDto), GetHourlySchedule}
        };

    public Guid Id { get; private set; }
    public string Message { get; private set; }
    public StatusEnum Status { get private set; }
    public Schedule Schedule { get; private set; }

    public Delivery(DeliveryDto dto){
        Id = dto.Id;
        Message = dto.Message;
        Status = StatusEnum.New;

        Schedule = ScheduleFactories[dto.Schedule.GetType()](dto.Schedule);
    }

    public void Update(DeliveryDto dto){
        Message = dto.Message;
        Schedule.Update(dto.Schedule);
    }

    public DateTime GetNextExecution(DateTime after){
        return Schedule.GetNextExecution(after);
    }

    public enum StatusEnum { New, InProgress, Complete }

    Schedule GetDailyScheduleEntity(ScheduleDto dto){
        return new DailyScheduleEntity((DailyScheduleDto)dto);
    } 

    Schedule GetHourlyScheduleEntity(ScheduleDto dto){
        return new HourlyScheduleEntity((HourlyScheduleDto)dto);
    }  
}

abstract class Schedule {
    public DateTime StartsOn { get; private set; }

    public abstract DateTime GetNextExecution(DateTime after);

    public virtual void Update(ScheduleDto dto){
         StartsOn = dto.StartsOn;
    }

    public Schedule(ScheduleDto dto) {
        StartsOn = dto.StartsOn;
    } 
}

class DailySchedule : Schedule {
    public DaysEnum Days { get; private set; }

    public override DateTime GetNextExecution(DateTime after){
       return // logic for calculating the next day based on Days;
    }

    public DailySchedule(DailyScheduleDto dto)
        : base(dto)
    { 
        Days = dto.Days.ToEnum<DaysEnum>();
        base.Update(dto);
    }

    public override void Update(ScheduleDto dto){
        if(!(dto is DialyScheduleDto)) throw new InvalidOperationException();
        Days = ((DailyScheduleDto)dto).Days.ToEnum<DaysEnum>();
    }

    [Flags]
    public enum DaysEnum{
        Monday, Tuesday, Wednesday, Thursday, Friday
    }
}

class HourlySchedule : Schedule {
    public int Interval { get; private set }

    public override DateTime GetNextExecution(DateTime after){
       return // logic for calculating the next hour based on interval;  
    }

    public HourlySchedule(HourlyScheduleDto dto)
        : base(dto)
    {
         Interval = dto.Interval;
    }

    public override void Update(ScheduleDto dto){
        if(!(dto is HourlyScheduleDto)) throw new InvalidOperationException();
        Interval = ((HoulryScheduleDto)dto).Interval;
        base.Update(dto);
    } 
}

class DeliveryService : IDeliveryService {
     public void Add(DeliveryDto dto){
         var delivery = new Delivery(dto);

         var scheduledTask = new ScheduledTask(
             delivery.Id,
             delivery.GetNextExecution(after: DateTime.Now)
         );

         unitOfWork.Add<ScheduledTask>(scheduledTask);
         unitOfWork.Add<Delivery>(delivery);
         unitOfWork.SaveChanges();
     }

     public void Update(DeliveryDto dto){
          var delivery = unitOfWork.Get<Delivery>(dto.Id);
          delivery.Update(dto);
          unitOfWork.SaveChanges();
     } 

     public void SendAll(){
         var scheduledTasks = unitOfWork.GetAll<ScheduledTask>(st => st.ExecutionDateTime < DateTime.Now && st.Status == ScheduledTaskStatus.New);
         scheduledTasks.ForEach(//Send to subsystem for execution);

     }
}

合同

public class DeliveryDto{
    public Guid Id {get; set;}
    public string Message {get; set;}
    public ScheduleDto Schedule {get; set;} 
}

public class ScheduleDto{    
    public DateTime StartsOn {get; set;} 
}

public class HourlyScheduleDto : ScheduleDto{
    public int Interval {get; set;} 
}

public class DailyScheduleDto : ScheduleDto{
    public String Days {get; set;} 
}

我通常会使用特定的 dto 来创建新实例和执行更新,但为了简单起见,在此处重复使用相同的类型。

虽然有一种观点认为将 Dtos 直接传递给 Entities 会使客户端与域紧密耦合,但我的意图是提供一个瘦的外观类型层,该层将负责“升级”dtos 以保持与现有客户端的兼容性。当前 dto 需要进行更改以适应不断变化的业务需求

【问题讨论】:

    标签: c# domain-driven-design soa


    【解决方案1】:

    一些想法:

    • 设计似乎过于复杂。 DailyScheduleHourlySchedule 都继承了 Schedule,但在它们的 Update() 方法中,您必须手动将参数转换为不同的类型,这是一种滥用继承的味道。

      另外,Delivery 包含一个调度工厂字典似乎是可疑的。我能看到的唯一原因是当您必须生成多种不同类型的多个对象时,但Delivery 似乎只需要一个Schedule 对象。

    • 我在Delivery 中执行Schedule.Update(dto.Schedule) 时发现了一个潜在的错误,并且计划不是初始化Delivery 的类型(每小时而不是每天,反之亦然)。

    • 总体而言,代码未能传达 IMO 的域意图。 Update 含糊不清——用户需要更新什么以及出于什么原因? after 变量名称是神秘的。顾名思义,为什么GetNextDeliverySchedule() 返回的是 DateTime 而不是 Schedule ?等等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-05
      • 2010-10-05
      • 1970-01-01
      • 2020-10-26
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      相关资源
      最近更新 更多