3

我目前工作的應用程序,需要每一個對數據庫所做的另一個表進行審覈:DDD審計?域層或存儲庫層?

例如:Employee表中有EmployeeAuditTable

我一直在辯論上,我應該把審覈功能! DDD之後,任何人都可以向我提供他們的建議和意見。

我想到的選項如下 1.)當「Repository」調用保存更改時,我應該從存儲庫執行審覈邏輯(這是不好的設計/實踐,是否不僅需要Repository持續的變化,但也仍然存在審覈的詳細信息,以及它甚至很好的做法,有一個從倉庫(IAuditService在這種情況下))

例1中被稱爲服務:?

public class EmployeeRepository : IRepository 
{ 
    DbContext _db; 
    IAuditService _auditService; 

    EmployeeRepository(IAuditService auditService) 
    { 
     _auditService = auditService 
    } 
    void Update(Employee empl) 
    { 
     // Update Employee with dbcontext entity framework 

     // Perform Audit using AuditService 
    } 

    void SaveChanges() 
    { 
     // Call save changes on dbcontext 
    } 

} 

2)應我在我的應用程序服務中呼叫IAuditService

實施例2:

public class EmployeeService 
{ 
    IAuditService _auditService; 
    IUnitOfWork _unitOfWork; 
    IEmployeeRepository _repository; 

    EmployeeService(IAuditService auditService, IUnitOfWork unitOfWork, IEmployeeRepository repo) 
    { 
     _auditService = auditService; 
     _unitOfWork= unitOfWork; 
     _repo = repo; 
    } 

    void UpdateEmployee(int id, string name, int age) 
    { 
     // Get Employee 

     // Update Employee 

     // Audit Changes 

     // Commit Transaction 

    } 


} 

回答

4

我知道您希望爲數據庫中的所有對象提供審計線索,但我不會監督您問題的完整複雜性。您不清楚EmployeeEmployeeAuditTable的外觀,但選擇的命名約定表明它包含與僱員表相同的列。

審覈可以並且通常被認爲是「橫切關注」。當您有諸如「所有更改應該被審覈」的要求時,情況尤其如此。如果審計不是業務問題(或用例或任何您稱之爲的事務),則應將其放入您的實體,服務或存儲庫中;如果僅僅因爲在審計過程中忘記編碼非常容易,就會給您帶來不正確的審計線索 - 有些人認爲這種審計線索比根本沒有審計線索更糟糕。

形成您的代碼示例,我看到您正在使用一個工作單元。我想,在

// commit transaction 

你提交由工作單位跟蹤的變化:

// commit transaction 
_unitOfWork.Commit(); 

再次,它承諾在工作單位跟蹤的變化。這是您的審計掛鉤點,它不需要涉及您的任何服務,實體或存儲庫中的代碼。實際上,當你使用像(N)Hibernate這樣的ORM框架時,你可以讓ORM跟蹤你的變化(它會掛鉤到它的工作單元),例如參見the wiki page "creating an audit log using events"或者Envers NHibernate的審計框架,在this answer on SO也討論過。我強烈建議您閱讀Envers文檔,即使您實施自己的審覈解決方案。

0
public class EmployeeRepository : IRepository 
{ 
    DbContext _db; 
    IAuditService _auditService; 

    EmployeeRepository(IAuditService auditService) 
    { 
     _auditService = auditService 
    } 

我不會被具有DAO使用AuditService創建我DAO和服務包之間的循環依賴關係。我猜AuditService依賴於AuditEventRepository或類似的東西?程序包之間的循環依賴關係(例如SomeRepository => AuditService => AuditRepository)使得將來難以重構。

我對你的問題領域一無所知,但如果你正在考慮創建一個單獨的AuditService,那麼問題域可能有圍繞審計的業務規則?例如,通過用戶類型審計事件,自動填充相關的上下文信息等。如果是這種情況,我會堅持封裝邏輯的單獨服務。

您是否考慮過面向方面的方法,而不是修改每個DAO或服務類?基於AOP的方法將減少您需要編寫的代碼量。它還可以使將來從截取服務調用切換到攔截DAO調用變得容易。

不管你把審計代碼,不管你是否調用審計在每個服務手動或使用AOP的,有一個超級重要的細節需要注意的:確保它在同一個數據庫事務的範圍內發生商業運作。這樣,所有內容都會一起提交或回滾,並且不會爲未發生的事件或缺少審計條目的事件提供審計條目。

3

我在自己的工作中遇到過類似的設計問題,這裏是我目前的理解。

您選擇的解決方案應基於您的業務規則/問題集。

在示例1中,您將EmployeeRepositoryIAuditService緊密結合在一起。如果您絕對必須對每一位員工的變更進行審計,並且忘記這樣做可能意味着可怕的後果,這可能很好。如果您不想使用某種FakeAuditService進行審計,那麼任何使用存儲庫(包括單元測試)的人都需要有意識地選擇退出審計。

在示例2中,您將AuditService作爲EmployeeService的責任。通過這樣做,您只會將數據庫訪問邏輯放在EmployeeRepository中,並讓EmployeeService擔心審計。這應該使得EmployeeRepository的實現和使用更簡單,並且在使用EmployeeRepository時您將具有更大的靈活性。但是,如果有人創建依賴於IEmployeeRepository的其他服務,他們可能會忘記將審計邏輯添加到該服務。

我個人比較喜歡示例2.考慮單一責任原則,EmployeeRepository的責任應該是簡單的數據訪問,而EmployeeService應該保留有關員工的業務邏輯。此業務邏輯包括審覈更改。