跟踪複雜對像圖中的變化
我開始考慮在斷開連接的應用程序中跟踪複雜對像圖的變化。我已經找到了幾個解決方案,但我想知道是否有任何最佳實踐或您使用什麼解決方案以及為什麼?我將同樣的問題傳遞給MSDN 論壇,但我只收到一個答案。我想從其他開發人員的經驗中獲得更多答案。
這個問題與 .NET 有關,因此對於實現細節的答案,我更喜歡與 .NET 世界相關的答案,但我認為這在其他平台上是相同的。
在我的案例中,理論問題是在多層架構中定義的(目前不一定是 n 層),如下所示:
- 使用 ORM 處理持久性的儲存庫層(ORM 工具目前無關緊要,但很可能是 Entity Framework 4.0 或 NHibernate)。
- 一組表示領域對象的純類(持久無知 = POCO,相當於 Java 世界中的 POJO)。儲存庫保存這些類並將它們作為查詢結果返回。
- 與域實體一起工作的域服務集。
- 門面層定義業務邏輯的網關。它在內部使用儲存庫、域服務和域對象。域對像不暴露——每個外觀方法使用一組專門的數據傳輸對像作為參數和返回值。將域實體轉換為 DTO 是每個外觀方法的責任,反之亦然。
- 使用外觀層和 DTO 的現代 Web 應用程序——我稱之為斷開連接的應用程序。一般來說,未來設計可能會發生變化,因此外觀層將被 Web 服務層包裹,而 Web 應用程序將使用該服務 => 過渡到 3 層(Web、業務邏輯、數據庫)。
現在假設域對象之一是具有訂單詳細資訊(行)和相關訂單的訂單。當客戶請求編輯訂單時,它可以修改訂單、添加、刪除或修改任何訂單詳細資訊以及添加或刪除相關訂單。所有這些修改都是在 Web 瀏覽器中的數據上完成的——javascript 和 AJAX。因此,當客戶端按下保存按鈕時,所有更改都會一次性送出。問題是如何處理這些變化?儲存庫和 ORM 工具需要知道哪些實體和關係被修改、插入或刪除。我以兩個“最佳”解決方案結束:
- 將 DTO 的初始狀態儲存在隱藏欄位中(在最壞的情況下儲存到會話中)。當接收到保存更改的請求時,根據收到的數據創建新的 DTO,並根據持久的數據創建第二個 DTO。合併這兩個並跟踪更改。將合併的 DTO 發送到外觀層,並使用收到的有關更改的資訊來正確設置實體圖。這需要在域對像中進行一些手動更改跟踪,以便可以從頭開始設置更改資訊,然後將其傳遞到儲存庫 - 這是我不太滿意的一點。
- 根本不跟踪 DTO 中的更改。在外觀層接收修改的數據時,創建修改的實體並從儲存庫載入實際狀態(通常是對數據庫的附加查詢 - 這是我不太滿意的一點) - 合併這兩個實體並通過 ORM 工具提供的實體代理自動跟踪更改(實體框架 4.0 和 NHibernate 允許這樣做)。並發處理需要特別注意,因為實際狀態不必是初始狀態。
你怎麼看?你有什麼建議嗎?
我知道可以通過在某些應用程序層上使用記憶體來避免其中一些挑戰,但這是我目前不想使用的東西。
我對這個話題的興趣更進一步。例如,假設應用程序採用 3 層架構並且客戶端(Web 應用程序)不會用 .NET 編寫 = DTO 類不能被重用。跟踪 DTO 上的更改將變得更加困難,因為它需要其他開發團隊在他們的開發工具中正確實施跟踪機制。
我相信這些問題在很多應用程序中都必須解決,請分享您的經驗。
這都是關於責任的。
(我不確定這是否是您想要的答案 - 如果不是,請告訴我,以便我更新它)。
所以我們在一個系統中有多個層——每個層負責不同的任務:數據訪問、UI、業務邏輯等。當我們以這種方式建構系統時,我們(除其他外)試圖通過使未來的變化變得容易每個組件負責一項任務——因此它可以專注於該一項任務並做好它。隨著時間的推移和需要更改,它還可以更輕鬆地修改系統。
在考慮 DTO 時需要牢記類似的想法——“如何跟踪更改?” 例如。我是這樣處理的: BL 負責管理規則和邏輯;考慮到網路的無狀態特性(這是我大部分工作的地方),我只是沒有跟踪對象的狀態並明確地尋找變化。如果使用者將數據傳回(要保存/更新),我將全部傳回而不關心更改了什麼。
一方面,這可能看起來效率低下,但由於數據量不是很大,所以這不是問題;另一方面,由於過程更簡單,因此“移動元件”更少出錯。
我如何將數據傳回?-
- 我使用 DTO(或者 POCO 會更準確);當我在 BL 和 DAL 之間交換數據(通過介面 / DI)時,數據作為 DTO(或它們的集合)進行交換。具體來說,我將一個結構用於單個實例,將這些結構的集合用於多個。
- DTO 是在一個具有很少依賴關係的公共類中定義的。
- 我故意嘗試限制為特定對象(如“訂單”)創建的 DTO 的數量——但同時,如果有充分的理由,我會創建新的。通常我會有一個“胖”DTO,其中包含該對象的大部分/所有可用數據,我也可能有一個更精簡的設計用於集合(用於列表等)。在這兩種情況下,這些 DTO 都是純粹的,用於返回“閱讀”資訊。你必須牢記責任——當 BL 請求數據時,它通常不會同時嘗試寫回數據;因此,DTO 是“只讀”的這一事實更多地是為了符合乾淨的介面和架構,而不是業務規則。
- 我總是為插入和更新定義單獨的 DTO——即使它們共享完全相同的欄位。這樣,可能發生的最壞情況是重複一些瑣碎的程式碼——而不是有依賴關係和多個重用案例來解開。
最後——不要混淆 DAL 的工作方式和 UI 的工作方式;讓 ORM 做他們的事,僅僅因為他們以給定的方式儲存數據並不意味著它是唯一的方式。
最重要的是在層之間指定有意義的介面。
管理變化是 BL 的工作;讓 UI 以最適合您的使用者的方式工作,並讓 BL 弄清楚它想如何處理它,而 DAL(通過您與DI的漂亮乾淨的界面)只是按照它的指示行事。