2016-05-26 22 views
2

我正在通過閱讀Eric Evans和Vaughn Vernon的書籍逐步學習DDD方法,並嘗試在我的項目中使用PHP實現它(但這裏真的無所謂)。如何在DDD方法中建模關聯?

最近我一直在閱讀了很多AggregateRoot實體模式的,應該由一個域中定義的模型。坦率地說,我不確定我是否理解所有的定義,所以我決定在這裏提出我的問題。

起初我想介紹一下我的(子)域負責員工的假期管理,這應該爲我的問題解答更容易。

最瑣碎的情況是Employee可以在許多Teams中找到。當員工決定休息幾天時,他必須發送HolidaysRequest的元數據,如假期類型(如休息假期,休息幾天以照顧他的孩子等),接受狀態以及課程時間範圍他不會出現在他的辦公室裏。應該知道哪個Employee發送了HolidaysRequest。我還想找到由Employee發送的所有HolidaysRequest

我很確定像DateRangeHolidayType是純粹的ValueObjects。對我來說很清楚。當我必須定義實體的邊界時,問題就開始了。我可能通過在實體中嵌套對象來定義關聯的不良做法,所以,請告訴我在這裏找出責任的定義。

  1. 這是什麼實體?什麼應該是一個Aggregate和AggregateRoot的地方在哪裏?
  2. 如何定義實體之間的關聯?例如。一個Employee可以屬於多個Team s或HolidaysRequestEmployee編寫並分配給另一個Employee誰可以接受它。它們是否應該作爲集合來實施?

爲什麼我問這些問題?因爲幾周前我在這裏發佈了一個問題,其中一個答案是考慮EmployeeTeams之間的關係,他們應該在名爲EmployeeInTeam的單個Aggreate中,但我不確定我是否以正確的方式理解它。

感謝您的任何建議。

+1

員工團隊如何與假期請求相關?這裏保護的不變量是什麼?例如,如果同一團隊的其他員工要求假日重疊,員工是否可以申請假期?等等,如果不知道你想要保護什麼不變量,你就無法定義正確的AR邊界。 – plalx

+0

@plalx假設沒有邊界。比方說,可能有一天,沒有人在某一天工作。另一種情況:假設團隊中至少有一個人必須在工作,而其他人則需要甜蜜的假期。我唯一需要知道的是如何建立這些關係的模型。員工不能休息的情況是某種事件的可承受性,不是嗎?另一方面,發出此類請求的員工必須知道他不能發送假期請求,因爲當天沒有員工可以使用。你怎麼看? –

+0

@plalx爲了完成我的回答,員工是發送假期請求的作者,所以另一個'Employee'將有一定的權限接受它,知道是誰發送了它。所以基本上'HolidaysRequest'由一些HolidaysRequestMetadata和'Employee'組成。 –

回答

0

關於DDD的主要問題是將焦點放在域中,這就是爲什麼它稱爲Domain Driven設計。

當您開始詢問關係,聚合和實體時,甚至沒有深入探究包含域的內容,您實際上是在尋找數據庫建模而不是域。

請問,我不是說你在問錯誤的問題,也不是在批評他們,我認爲你在學習的過程中嘗試付諸實踐並沒有錯。

我不是DDD專家,我和你一樣學習,但我會盡力幫忙。

從思考什麼情況可能會出現關於Holydays管理開始。當你有不同的規則時,你可以從策略開始(我說的是最終的解決方案)。

建立一個好的和有意義的域名是非常困難的(至少對我而言)。你寫代碼。測試它。有洞察力,拋出你的代碼並重寫它。重構它。在軟件的生命週期中,你應該把重點放在域上,所以你應該一直在改進它。

開始編碼(如域的草稿),看看它是怎樣的。讓我們鍛鍊它。首先,爲什麼我們需要管理這些東西?我們試圖解決什麼問題?啊,有時候員工會問一些休息日,我們想控制它。我們可能會批准或不批准,這取決於他們希望「holyday」的原因,我們的團隊狀態如何。如果我們拒絕,他們仍然回家,我們會遲到決定我們是否打工或打工。執法無處不在的語言,讓我們在代碼中表達這個問題:

public interface IHolydayStrategy 
{ 
    bool CanTakeDaysOff(HolydayRequest request); 
} 

public class TakeCareOfChildren : IHolydayStrategy 
{ 
    public bool CanTakeDaysOff(HolydayRequest request) 
    { 
     return IsTotalDaysRequestedUnderLimit(request.Range.TotalDays()); 
    } 

    public bool IsTotalDaysRequestedUnderLimit(int totalDays) 
    { 
     return totalDays < 3; 
    } 
} 

public class InjuredEmployee : IHolydayStrategy 
{ 
    public bool CanTakeDaysOff(HolydayRequest request) 
    { 
     return true; 
    } 
} 

public class NeedsToRelax : IHolydayStrategy 
{ 
    public bool CanTakeDaysOff(HolydayRequest request) 
    { 
     return IsCurrentPercentageOfWorkingEmployeesAcceptable(request.TeamRealSize, request.WorkingEmployees) 
      || AreProjectsWithinDeadline(request.Projects); 
    } 

    private bool AreProjectsWithinDeadline(IEnumerable<Project> projects) 
    { 
     return !projects.Any(p => p.IsDeadlineExceeded()); 
    } 

    private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees) 
    { 
     return workingEmployees/teamRealSize > 0.7d; 
    } 
} 

public class Project 
{ 
    public bool IsDeadlineExceeded() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class DateRange 
{ 
    public DateTime Start { get; set; } 
    public DateTime End { get; set; } 

    public int TotalDays() 
    { 
     return End.Subtract(Start).Days; 
    } 

    public bool IsBetween(DateTime date) 
    { 
     return date > Start && date < End; 
    } 
} 

public enum HolydayTypes 
{ 
    TakeCareOfChildren, 
    NeedToRelax, 
    BankOfHours, 
    Injured, 
    NeedToVisitDoctor, 
    WannaVisitDisney 
} 

public class HolydayRequest 
{ 
    public IEnumerable<Project> Projects { get; internal set; } 
    public DateRange Range { get; set; } 
    public HolydayTypes Reason { get; set; } 
    public int TeamRealSize { get; internal set; } 
    public int WorkingEmployees { get; internal set; } 
} 

下面是我很快寫了這個:

  • Holydays可授予與否,視情況而定,並 原因,讓我們創建一個IHolydayStrategy
  • 創建一個空的(無產階級HolydayRequest類。
  • 對於每個可能的原因,讓我們創建一個不同的策略。
  • 如果原因是要照顧孩子,他們可以休息幾天,如果 總的天數要求是在限制之下。
  • 如果原因是因爲員工受傷,除了允許請求外,我們沒有 選擇。
  • 如果原因是因爲他們需要放鬆,我們檢查我們是否有 可接受的工作僱員百分比,或者是否在 截止日期之內。
  • 只要我在戰略中需要一些數據,我就使用CTRL + .到 自動創建HolydayRequest中的屬性。

看看我怎麼不知道這些東西是如何存儲/映射的?我只是編寫代碼來解決問題,並獲取解決問題所需的信息。

顯然這不是最終的領域,只是一個草案。如果需要,我可能會拿走這段代碼並重寫,但現在還沒有感覺。

人們可能認爲這是沒用的,創建一個InjuredEmployee班只是始終返回true,但這裏的關鍵是要利用通用語言的,把事情的明確越好,任何人都會閱讀和理解同樣的事情:「好吧,如果我們有一名受傷員工,他們總是可以休息幾天,不管團隊情況以及他們需要多少天。」「。 DDD解決這個概念時遇到的問題之一是開發人員,產品所有者,領域專家和其他參與者之間對術語和規則的誤解。

之後,我會開始用模擬數據編寫一些測試。我可能會重構代碼。

這 「3」:

public bool IsTotalDaysRequestedUnderLimit(int totalDays) 
    { 
     return totalDays < 3; 
    } 

而這個 「0.7D」:

private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees) 
    { 
     return workingEmployees/teamRealSize > 0.7d; 
    } 

的規格,在我看來,這不應該存在於一個戰略點。我們可能會應用規範模式來解耦。

當我們通過測試得到合理的初始解決方案後,現在讓我們考慮應該如何存儲它。我們可以在這裏使用最終定義的類(比如Team,Project,Employee)來映射ORM。

只要你開始寫你的域,關係將出現你的實體之間,這就是爲什麼我通常不關心所有的ORM將如何堅持我的域名,什麼是在這一點上聚集。

看看我還沒有創建一個Employee類,儘管這聽起來很重要。這就是爲什麼我們不應該從創建實體及其屬性開始,因爲它與創建表和字段完全相同。

你的DDD變成數據庫驅動設計那樣,我們不想這樣。當然,最終我們會成爲員工,但讓我們一步一步,只在需要時才創建。不要試圖立即開始建模,預測所有您需要的實體。把重點放在你的問題上,以及如何解決它。

關於你的問題,什麼是實體和什麼是聚合,我認爲你不是問他們的定義,而是考慮你的域名是否被認爲是一個或另一個。只要您的域名開始被您的代碼揭示,您最終會回答自己。當你開始開發你的應用程序層時,你應該知道它,它應該負責加載數據並委託給你的域。我的域名邏輯預期哪些數據,從哪裏開始查詢。

我希望我幫了別人。

+0

感謝您的好評!與此同時,在互聯網上閱讀關於聚合的信息,特別是在事件採購方面。據我瞭解你所寫的有關關聯(或缺乏關聯),我開始思考如何考慮事件之間的一致性。假設有一位員工在12月31日請假(今天是6月21日)。這是長期居住的事件。員工團隊名稱變更或員工變更職位時會發生什麼?我是否應該保持對團隊身份的引用以檢索最新的按需價值? –

+0

@KubaT完美的例子。這是我想問的域名專家的問題。我們如何創建關聯,聚合取決於他們對這種情況的理解。據我所知,談到事件採購,你永遠不會*刪除*的東西。他們會在那裏,所以你的全新員工(屬於新的團隊或者已經改變了他們的職位)可能不再有這個Holyday了。我也在學習這個概念,對我來說這有點新鮮。如果你得出結論,請告訴我,我很高興如果我能以某種方式幫助。 – Alisson

+0

ES中的「不刪除」事情是顯而易見的,但我更願意考慮關聯的實體更改問題。看看我的示例,當放置HolidaysRequest並且它包含Employee數據時,員工更改時該怎麼辦?我無法搜索該具體員工發生的EventStore中發生的所有事件。即使它是像HolidaysWithEmployee這樣的聚合,我也無法僅僅因爲員工數據已經改變而更新它。對? –

相關問題