4

我正在使用 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));
    }
}

然后我有一个聚合方法,允许使用规范添加新窗口。已经持久化的聚合窗口被传递到规范构造函数中。

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

我正在苦苦挣扎的是,我的一部分认为我应该将此规范注入到类中,而不是直接实例化(作为我的应用程序的一般规则,而不仅仅是在这种情况下),因为规范的类型可能需要根据实体的状态而改变。但是从 MVC 层注入规范感觉很脏,就好像我稍后有另一个应用程序接口(如 REST API)然后重复使用哪个规范的逻辑。

您如何确保所使用的规范保持灵活,同时确保有关使用哪个规范的逻辑不会在另一个应用程序界面中重复。

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

4

1 回答 1

2

将域服务注入实体是完全可以接受的。最好在聚合的相应方法中将对服务的依赖项明确作为参数。例如,该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);
        }
    }

在这种情况下,规范充当域服务。现在由周围的基础设施来通过适当的规范。这就是应用程序服务的用武之地。应用程序服务在您的领域层上建立一个外观,并包含特定用例的方法。反过来,该应用程序服务将被控制器引用。也可以让控制器传递所需的依赖关系,但是应用程序服务提供的封装可能是有益的。

示例应用程序服务代码:

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

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

于 2012-12-05T15:47:27.307 回答