域驱动设计和使用多态子实体

时间:2015-05-22 15:15:22

标签: c# domain-driven-design soa

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

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

THE DOMAIN

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;} 
}

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

虽然有一种观点认为直接将Dtos传递给实体会将客户端紧密地耦合到域中,但我的目的是提供一个薄的外观类型层来负责升级&#39;如果当前需要改变以适应不断变化的业务需求,dtos将保持与现有客户的兼容性

1 个答案:

答案 0 :(得分:1)

一些想法:

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

    此外,包含Schedule of Schedule工厂的Delivery似乎很可疑。我能看到的唯一原因是你必须生成多个不同类型的多个对象,但Delivery似乎只需要一个Schedule对象。

  • 我在Schedule.Update(dto.Schedule)中执行Delivery时看到了一个潜在的错误,并且时间表不是Delivery初始化的类型(每小时而不是每日,反之亦然) )。

  • 总的来说,代码无法传达域意图IMO。 Update含糊不清 - 用户需要更新什么以及出于什么原因? after变量名称含糊不清。为什么GetNextDeliverySchedule()会返回DateTime而不是名称?等等。