2012-12-04 40 views
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,稍後關於使用哪個規範的邏輯將被重複。

如何確保使用的規範保持靈活性,同時確保關於使用哪個規範的邏輯不會在其他應用程序界面中重複使用。

這是一種情況,您希望將工廠注入實體並從那裏返回規範,從而不允許域邏輯溢出到更高層?還是有更好/更清潔/更簡單的方法來做到這一點?

回答

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

這是典型的應用服務代碼:得到適當的聚合,實例化,如果需要任何依賴,並在總調用行爲。

+0

好的我看過這種架構在我正在閱讀的書中討論過。所以基本上我應該停止直接將存儲庫傳遞到我的控制器中,並用代表可以在域上執行的所有不同操作的方法創建服務,然後注入這些類。 –

+1

是的,這是一種常用的方法。但是,您應該意識到這並非嚴格要求。如果控制器是調用此行爲的唯一地方,則控制器本身可充當應用程序服務。在這種情況下唯一的問題是管制員責任和領域層責任的混合。總的來說,這是一個偏好問題。 – eulerfx

+0

是的,這是我所關心的,它感覺像域層的細節泄漏出來,以允許注入依賴和其他東西。非常感謝,現在我必須回去重構無數次哈哈。 –