Domain driven design 域驱动设计中规范类的正确使用

Domain driven design 域驱动设计中规范类的正确使用,domain-driven-design,Domain Driven Design,我正在使用DDD构建一个应用程序,但是我很难理解应该在哪里实例化规范类或使用它们 我的应用大量使用预订窗口,因此我有一个规范,确保即将添加到聚合中的预订窗口不会与当前聚合中的另一个窗口重叠。像下面这样 /// <summary> /// A specification that determines if the window passed in collides with other windows. /// </summary> public class Bo

我正在使用DDD构建一个应用程序,但是我很难理解应该在哪里实例化规范类或使用它们

我的应用大量使用预订窗口,因此我有一个规范,确保即将添加到聚合中的预订窗口不会与当前聚合中的另一个窗口重叠。像下面这样

    /// <summary>
/// A specification that determines if the window passed in collides with other windows.
/// </summary>
public class BookingTemplateWindowDoesNotCollideSpecification : ISpecification<BookingScheduleTemplateWindow>
{
    /// <summary>
    /// The other windows to check the passed in window against.
    /// </summary>
    private readonly IEnumerable<BookingScheduleTemplateWindow> otherWindows;

    /// <summary>
    /// Initializes a new instance of the <see cref="BookingTemplateWindowDoesNotCollideSpecification" /> class.
    /// </summary>
    /// <param name="otherWindows">The other windows.</param>
    public BookingTemplateWindowDoesNotCollideSpecification(IEnumerable<BookingScheduleTemplateWindow> otherWindows)
    {
        this.otherWindows = otherWindows;
    }

    /// <summary>
    /// Determines whether the window passed in collides with other windows held inside this class.
    /// </summary>
    /// <param name="obj">The obj.</param>
    /// <returns>
    ///  <c>true</c> if [is satisfied by] [the specified obj]; otherwise, <c>false</c>.
    /// </returns>
    public bool IsSatisfiedBy(BookingScheduleTemplateWindow obj)
    {
        return !this.otherWindows.Any(w => obj.DayOfWeek == w.DayOfWeek && w.WindowPeriod.IsOverlap(obj.WindowPeriod));
    }
}
//
///确定传入的窗口是否与其他窗口冲突的规范。
/// 
公共类BookingTemplateWindow不符合规格说明:I规格说明
{
/// 
///用于检查传入窗口的其他窗口。
/// 
私有只读IEnumerable otherWindows;
/// 
///初始化类的新实例。
/// 
///其他的窗户。
public BookingTemplateWindow不符合规格(IEnumerable Other Windows)
{
this.otherWindows=otherWindows;
}
/// 
///确定传入的窗口是否与该类中的其他窗口冲突。
/// 
///obj。
/// 
///如果[满足][指定的obj],则为true;否则为false。
/// 
公共bool由(BookingScheduleTemplateWindow对象)满足
{
return!this.otherWindows.Any(w=>obj.DayOfWeek==w.DayOfWeek&&w.WindowPeriod.IsOverlap(obj.WindowPeriod));
}
}
然后我在聚合上有一个方法,允许使用规范添加一个新窗口。已持久化的聚合窗口被传递到规范构造函数中

        public virtual void AddWindow(DayOfWeek dayOfWeek, int startTime, int endTime)
    {
        var nonCollidingWindowSpecification = new BookingTemplateWindowDoesNotCollideSpecification(this.Windows);
        var bookingWindow = new BookingScheduleTemplateWindow(this){
                                                                       DayOfWeek = dayOfWeek,
                                                                       WindowPeriod = new Range<int>(startTime, endTime)
                                                                   };

        if (nonCollidingWindowSpecification.IsSatisfiedBy(bookingWindow))
        {
            this.Windows.Add(bookingWindow);
        }
    }
public virtual void AddWindow(DayOfWeek DayOfWeek,int startTime,int endTime)
{
var nonCollidingWindowSpecification=new bookingtemplateWindow不符合指定(this.Windows);
var bookingWindow=新建bookingschedulemplateWindow(此){
DayOfWeek=DayOfWeek,
WindowPeriod=新范围(开始时间、结束时间)
};
if(非CollidingIndowSpecification.Issatifiedby(bookingWindow))
{
此.Windows.Add(bookingWindow);
}
}
我正在努力解决的是,我的一部分人认为我应该将此规范注入类中,而不是直接实例化(作为我的应用程序的一般规则,而不仅仅是在本例中),因为规范的类型可能需要根据实体的状态而改变。但是从MVC层注入规范会让人感觉不舒服,好像我以后有了另一个应用程序接口,比如RESTAPI,那么关于使用哪个规范的逻辑将被复制

如何确保所使用的规范保持灵活性,同时确保关于要使用的规范的逻辑不会在另一个应用程序接口中重复


在这种情况下,您是否希望将工厂注入实体并从实体返回规范,从而不允许域逻辑溢出到更高的层?或者有更好/更干净/更简单的方法来实现这一点吗?

将域服务注入实体是完全可以接受的。最好将对服务的依赖项显式地作为聚合上相应方法中的参数。例如,
AddWindow
方法可以如下所示:

  public virtual void AddWindow(ISpecification<BookingScheduleTemplateWindow> nonCollidingWindowSpecification, DayOfWeek dayOfWeek, int startTime, int endTime)
  {
        var bookingWindow = new BookingScheduleTemplateWindow(this){
                                                                       DayOfWeek = dayOfWeek,
                                                                       WindowPeriod = new Range<int>(startTime, endTime)
                                                                   };

        if (nonCollidingWindowSpecification.IsSatisfiedBy(bookingWindow))
        {
            this.Windows.Add(bookingWindow);
        }
    }

这是典型的应用程序服务代码:获取适当的聚合,实例化所需的依赖项(如果有的话),并调用聚合上的行为。

Ah好的,我在我正在阅读的一本书中看到过这种体系结构。因此,基本上我应该停止将存储库直接传递到我的控制器中,使用表示可以在域上执行的所有不同操作的方法创建服务,然后注入这些类。但是,您应该知道,这并不是严格要求的。如果控制器是调用此行为的唯一位置,则控制器本身可以充当应用程序服务。这种情况下唯一的问题是控制器责任和域层责任的合并。总的来说,这是一个偏好的问题。是的,这正是我所关心的,它感觉域层的详细信息正在泄漏,以便允许注入依赖项和其他东西。非常感谢,现在我必须回去重构第无数次了哈哈。我的印象是服务协调实体之间的工作,而不是实体之间的工作?@jgauffin这对于应用程序服务来说当然是正确的-实体不应该引用应用程序服务。然而,在某些情况下,实体引用域服务是有意义的。我认为这是可以接受的,因为正如您可以将数据传递给实体以便它们执行行为一样,您传递的域服务通常充当数据源的抽象。
public class AddWindow(string aggregateId, DayOfWeek dayOfWeek, int startTime, int endTime)
{
    var aggregate = this.repository.Get(aggregateId);
    var specification = // instantiate specification
    aggregate.AddWindow(specification, dayOfWeek, startTime, endTime);
    this.repository.Commit();
}