簡體   English   中英

如何為兩個域制作通用存儲庫以避免添加依賴項?

[英]How to make generic repository for two domains to avoid adding dependencies?

我正在制作一個具有兩個獨立域的應用程序,其中包含代表不同概念的模型:

架構圖

現在,我想創建通用存儲庫,它允許我對數據集進行操作,並且我希望它被兩個 DataAccess 項目使用,因為 DRY。 問題是,我會以使用實體 id 的方式編寫這些存儲庫,因此我需要基本類型或至少允許我對這些存儲庫進行操作的接口。 我將使用包含 ID 的BaseEntity創建通用存儲庫:

public class GenericRepository<T> where T: BaseEntity
{
   public T SomeActionThatRequiresId(int id)
   {
     // something that requires ID from BaseEntity
   }
}

問題是我不知道把BaseEntity放在哪里。

總是有人說領域項目不應該有依賴關系,所以我不想創建單獨的“基礎”項目來引用,以跟上這個想法。

如果我想跟上上述規則,將它放在一個域項目中然后在另一個域項目中引用它甚至不算數。

我不知道如何正確地做到這一點。

任何人都可以幫忙嗎?

不用說,我認為您可能高估了 DRY 作為校長的重要性,並試圖過早地定義共同的基礎。 DRY 的一個主要限制是當它導致依賴問題時,您似乎在很早的設計階段就遇到了這種問題。

如果 Config 和 Core 確實是獨立的域,那么它們的數據訪問模式似乎很可能會隨着時間的推移而發生分歧。 在開發的早期至少重復一次,然后等着看它們是否會出現分歧,這並沒有錯。 如果它們總是需要同步更改,您可以隨時將它們合並到一個通用存儲庫中。

同樣,您的聲明:“總是有人說域項目不應該有依賴關系”應該被視為指南,而不是硬性規定。

如果您希望您的所有域實體都具有共同的屬性,也許除了 int id 字段之外還需要審計字段,那么公共基礎實體庫是顯而易見的解決方案,不應該被排除在外。

我寫這個答案是因為評論的字符數有限。 :)

據我了解,您並不是真的想找到實現接口的通用方法。 我認為對DRY存在普遍的誤解。 如果您創建兩個以類似方式實現的存儲庫(具有與GetById相同的方法),這不是DRY DRY是當您copy-pasting在相同上下文中執行完全相同操作的代碼時。

您有兩個完全不同的上下文,其中一個是Configuration ,第二個是Core 在這種情況下,您將違反Common Closure Principle因為存儲庫將共享相同的界面,而它們可能具有不同的更改軸。

根據您所描述的內容,我希望您確認您正確使用了 UML( DataAccess繼承了Core項目,或者您想顯示引用的方向是什么?所以CoreDataAccess引用了?)

我不知道這是否最適合您,但Data Access Layer應該引用Domain Layer 由於大多數時候Data Access僅用於保存Domain對象或檢索Domain對象,不應被任何其他地方引用。

在這種情況下,它看起來像這樣:

在此處輸入圖片說明

箭頭代表什么引用什么。 在這種情況下,您將IConfigRepo放入Config.Domain項目中,並且該Repository的實現在Config.DataAccessLayer中完成,這可以確保您的域提供合同並知道如何允許獲取數據但不知道如何存儲它們。 因此,您可以將它們存儲在 SQL 或 No-SQL 數據庫中。

知道你的Core項目只是委派工作來糾正Domain項目。 無論如何都可以完成,但您確定業務Buisness logic存儲在特定的Domain項目中。

關於使用抽象存儲庫來獲取記錄的想法。 這樣做會有什么好處? 你確定要允許polymorfic執行Core.Domain資料庫中那個在實施Config.DataAccess

我的回答分為兩部分...

第 1 部分:將 BaseEntity 放在哪里

把它放在一個單獨獨立的地方。

通過這種方式,域都可以依賴它,而不是相互依賴。

下面您可以看到一個多層架構,其中通常的可疑對象(UI、邏輯、數據訪問)被適當地分離。 他們需要共享的概念在位於旁邊的Common層中。 它被大多數其他項目/程序集引用,但這樣做是安全的,因為 Common 中的代碼相對更穩定。

例如,Poco 是愚蠢的數據結構,與(例如)使用它們的邏輯代碼相比,不太可能改變。 如果 Poco 確實需要更改,那么您很可能已經想要更改其他層了(例如,由功能更改驅動)。

因此,您應該將BaseEntity放在Common (或一些類似的域獨立位置)。

在此處輸入圖片說明

第 2 部分:您是否在解決正確的問題?

我得到的感覺(其他閱讀此主題的人的回應)是您可能關注錯誤的問題。 如果是這樣的話 - 沒關系。 這很容易做到,你不應該為此自責——或者更糟的是,陷入沉沒成本謬誤

問題是,一旦您擁有一個被廣泛使用的實現(例如您的通用存儲庫),它就會變得有約束力。 使用基類和繼承(特別是與接口相比時)是這個普遍問題的一個很好的例子。

在架構層面,可以通過幾種方式實現重用,一種是通過模式 在您的情況下,您需要做的就是確定一個模式(或模式組合),它可以很好地滿足您的需求和優先級(例如代碼可維護性、滿足功能需求的能力、靈活性——等等)。

重用這些模式意味着你具有一致性和清晰度,你可能有重復代碼的事實並不是一個問題,因為模式的好處超過了重復代碼的任何負面影響。

然后是單一職責原則(SRP) 擁有一個通用存儲庫會破壞 SRP,因為它試圖在所有領域中工作——每個領域都有不同的設計和需求,以不同的方式處理它們,這將與通用存儲庫的想法相沖突。

通用 repo 看起來只做一件事(數據訪問) - 但那一件事通常太大且太復雜。 說通用回購是“做 SRP”是一種錯覺。

我自己嘗試過類似的路徑(並在項目中觀察它們),您會發現將存在數據訪問邊緣情況,其中通用 repo 想法會妨礙您 - 例如當您進行跨域活動時,尤其是在運行時性能的情況下因為那些需求特別高。

使用 DRY 是好的,但它是眾多工具中的一種,它本身並不是目的,它的使用需要與其他考慮因素相平衡。

如果不了解您的項目細節,我們很難找到最佳解決方案。

但我會盡量給你一些選擇:

  1. 使用 BaseEntity 定義擴展您的核心庫。 好處:根據小圖,這似乎遵循您的基本設計,但也意味着您的 repo 模塊將依賴於核心庫。
  2. 引入定義 BaseEntity 的新中間層。 如果由於某種原因它不屬於現有的 Core 庫,您可以創建一個新的庫,該庫對兩個域都是通用的並從 Core 派生而來,並且在中間庫上具有相同的依賴關系問題。
  3. 您不創建 BaseEntity 而是 Repo 模塊以純函數形式提供必要的接口,例如 IRepoObject { getId(); 並且您的域對象實現了該接口。 這具有清晰的關注點分離。 (並且域模塊需要 repo 模塊不是問題,因為它們現在提供 repo 功能)
  4. 函數式編程方法:如果 repo 模塊需要的數據結構不是過於復雜,您只需將它們作為原語傳遞給函數調用,例如 doRepoStuff(String id, Byte[] blob, ...)。 所以 repo 模塊對於任何特定的類或接口實現都是不可知的。

根據我的經驗,簡單的解決方案通常比具有深度繼承和大量引用的復雜的面向對象設計更可取,因此我傾向於根據交換數據的復雜性使用解決方案 3 或 4。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM