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应该保留有关员工的业务逻辑。此业务逻辑包括审核更改。