2009-08-12 111 views
7

我正在使用具有貧血域模型的遺留系統。訪問遺留系統中存儲庫的重構域邏輯

該域具有以下實體類別:Car,CarType,CarComponent,CarComponentType

對於其中的每一個,都有一個單獨的存儲庫。還有一些服務可以訪問這些存儲庫並基本包含所有的邏輯。

我需要實施一種方法,確定供應商是否可以停止CarComponentType。邏輯如下:只有當今沒有該組件的現有汽車時,纔可以停止組件。

最初,我在一個服務類中實現了它。

public boolean canBeDiscontinued(CarComponentType carComponentType) { 
    List<Car> cars = carRepository.getCarsWithComponent(carComponentType); 
    return cars.isEmpty(); 
} 

這是有效的 - 但是這個邏輯從代碼中的其他地方使用。它可能會增長,它看起來像的東西,可能適合CarComponentType類,而不是:

public boolean canBeDiscontinued() { 
    List<Car> cars = carRepository.getCarsWithComponent(this); 
    return cars.isEmpty(); 
} 

但是,我不能把它放在那裏,因爲它需要訪問的資源庫(我的理解是實體知道數據訪問層的非常嚴重的反模式)。當加載組件類型時,我無法加載所有類型的汽車,因爲那可能是成千上萬的對象。我們沒有使用任何ORM,因此製作一個懶加載的集合不僅體積龐大,而且容易出錯。

像我第一次那樣在服務類中實際使用此方法更合適嗎?這不重要嗎?有另外一種選擇嗎?我應該從另一個起點開始重構嗎?

還有一個類似的問題here。但是我的問題涉及Java,所以我不認爲這個解決方案適用於我的情況。另外,對於使用汽車和組件作爲我的領域模型,提前抱歉。 :)

回答

3

這聽起來像一個很好的候選人爲Specification pattern

+0

在哪一層我會把規範的實施?基礎設施? – Chris 2010-01-05 14:48:30

+0

規範是域層的一個組成部分,因爲'域概念'是在規範中實現的。 – 2010-01-05 15:07:57

5

弗雷德裏克Gheysels的回答是不錯的,雖然也許有點短。爲了詳細說明:在你的情況,你可以通過defininig接口爲您的規範(原諒我的C#語法)開始了:

public interface ICarSpecification 
{ 
    bool IsSatisfiedBy(CarComponentType carComponentType); 
} 

然後,您可以創建一個使用您的信息庫ICarSpecification的implmenetation。事情是這樣的:

public class CanDiscontinueSpecification : ICarSpecification 
{ 
    private readonly CarRepository carRepository; 

    public CanDiscontinueSpecification(CarRepository carRepository) 
    { 
     this.carRepository = carRepository; 
    } 

    public bool IsSatisfiedBy(CarComponentType carComponentType) 
    { 
     return this.carRepository.GetCarsWithComponent(carComponentType).Any(); 
    } 
} 

你可以停在那兒,但我並不特別喜歡關於規範模式的一點是,它是不是很被發現。一個可能的解決方案是將規範注入CarComponentType本身:

public class CarComponentType 
{ 
    private readonly ICarSpecification discontinueSpec; 

    public CarComponentType(ICarSpecification discontinueSpec) 
    { 
     this.discontinueSpec = discontinueSpec; 
    } 

    public bool CanBeDiscontinued() 
    { 
     return this.discontinueSpec.IsSatisfiedBy(this); 
    } 
} 

另外,如果你不喜歡隨身攜帶的類的每個實例的規格,你可以使用方法注入,而不是構造器注入:

public bool CanBeDiscontinued(ICarSpecification spec) 
{ 
    return spec.IsSatisfiedBy(this); 
} 

這種方法並沒有真正在執行方面增加任何價值,但更多的發現。

+0

感謝您澄清。如果我有更多可以組合的規格,這似乎是適當的。就我而言,我不確定。我沒有爲每個實體注入一個存儲庫,而是注入了一個使用存儲庫的規範。這真的好多了嗎? – waxwing 2009-08-12 15:36:40

+2

好吧,它將您的類從存儲庫中分離出來,並且通過傳遞您想要進行測試的規範實現來進行單元測試可能更容易。 – 2009-08-12 16:51:32

+0

是的:什麼是克里斯科剛纔說的:)嚴格來說,這使得規範只是另一種策略,可能會或可能不依賴於存儲庫。不過,我還包括了方法注入替代方案,以防萬一您不希望對規範具有普遍依賴性。 – 2009-08-12 21:45:46

1

我不認爲這是輕描淡寫地說我討厭貧血的領域模型和下一個男人一樣多。

但是,鑑於您正在研究的系統已經建立了貧血領域模型/服務(反)模式,我認爲引入其他模式可能會導致系統在完成隔離病例時的影響,因爲我相信您在此處。這樣的決定應該由團隊做出,並且應該有明確的好處。另外,恕我直言,我認爲你的原代碼片段比其他答案中提出的解決方案更簡單(沒有進攻馬克和弗雷德裏克,只是一個觀察:-),更復雜的解決方案並沒有帶來任何好處 - 我的意思是,兩種情況下的功能都是相同的,但後來使用更多的移動部件

就如何進行而言,只用一個例子就很難說。引入一個ORM(你提到目前沒有使用)可能是一種方法,因爲它應該減少你的服務類中的代碼,並且會有明顯的好處,所以我會試着從那裏開始,然後一旦到位情況。

+0

謝謝。我幾乎已經自己辭去了這個答案,但是我爲了學術目的而想知道。我覺得*應該*是一個更聰明的解決方案,我只是沒有看到。 – waxwing 2009-08-12 15:30:32

3

我不認爲「這可以中止」屬於任何類別。誰負責確定零件是否可以停產?不是汽車或汽車組件。我認爲你在一個服務中實施的初始方法正處於正確的軌道上。也許你需要一個CarInventoryManagementService這是負責回答有關汽車的庫存物品這樣的問題:

carsUsingComponent(CarComponent comp) 
canComponentBeDiscontinued(CarComponent comp) 
etc 

如果你在代碼需要詢問庫存相關的問題,比如你的「canBeDiscontinued」多個地方,那麼它可能是有意義只是爲了創建一項服務。

+0

這有點主觀。域對象是否應該「意識到」客戶端如何使用它通常不是邊界情況。也許你在這種情況下是正確的。但讓我們說這是一個不同的功能 - 這無疑適合於CarComponentType,但需要數據不包含在對象本身中。這就是我更有趣的地方。 – waxwing 2009-08-13 13:46:51

+0

我在思考責任驅動設計。汽車是否會成爲負責確定它是否仍在生產或汽車的創造者?一輛汽車當然可以知道是誰製造的,但它確實沒有任何理由來決定是否所有的汽車都是生命的終結。域對象通常只應回答關於他們自己的問題。適用於所有類型的域對象的問題通常是域對象服務的責任。顯然我不知道這個軟件,但這是一個普遍的原則。 – 2009-08-13 17:37:49