我正在使用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);
}
}
我正在努力的是,我的一部分是认为我应该将这个规范注入到类中而不是直接实例化(作为我的应用程序的一般规则,而不仅仅是在这种情况下),因为可能需要这种类型的specfication根据实体的状态进行更改.但是从MVC层注入规范感觉很脏,好像我有一个像REST API这样的另一个应用程序接口,然后关于使用哪个规范的逻辑将被复制.
如何确保所使用的规范保持灵活性,同时确保使用哪个规范的逻辑不会在另一个应用程序界面中重复.
这是一种情况,您希望将工厂注入实体并从那里返回规范,从而不允许域逻辑溢出到更高层?或者有更好/更清洁/更简单的方法吗?
最佳答案 将域服务注入实体是完全可以接受的.最好将服务的依赖关系显式化为聚合中相应方法中的参数.例如,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();
}
这是典型的应用程序服务代码:获取适当的聚合,实例化所需的依赖项(如果有),并在聚合上调用行为.