Design patterns DDD审计?域层还是存储库层?

Design patterns DDD审计?域层还是存储库层?,design-patterns,domain-driven-design,repository-pattern,Design Patterns,Domain Driven Design,Repository Pattern,我当前使用的应用程序要求在另一个表中审核对数据库所做的每项操作: ex:Employee表有EmployeeAuditTable 我一直在讨论我应该把审计功能放在哪里!遵循DDD,任何人都可以向我提供他们的建议和意见 我想到的选项如下 1.)当“存储库”调用save changes时,我应该从存储库执行审计逻辑(让存储库不仅保留更改,而且还保留审计细节,这是一种糟糕的设计/做法吗?从存储库中调用服务(在本例中是IAuditService)是一种好做法吗?) 例1: public class Em

我当前使用的应用程序要求在另一个表中审核对数据库所做的每项操作:

ex:Employee表有EmployeeAuditTable

我一直在讨论我应该把审计功能放在哪里!遵循DDD,任何人都可以向我提供他们的建议和意见

我想到的选项如下 1.)当“存储库”调用save changes时,我应该从存储库执行审计逻辑(让存储库不仅保留更改,而且还保留审计细节,这是一种糟糕的设计/做法吗?从存储库中调用服务(在本例中是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

   }


}
我不会让DAO使用AuditService,从而在我的DAO和服务包之间创建循环依赖关系。我猜AuditService依赖于AuditEventRepository或类似的东西?包之间的循环依赖关系(例如SomeRepository=>AuditService=>AuditRepository)使将来的重构变得更加困难

我对您的问题域一无所知,但如果您正在考虑创建一个单独的AuditService,那么问题域是否有自己的审计业务规则?例如,按用户类型审核事件,自动填充相关上下文信息等。如果是这样,我会使用封装逻辑的单独服务

您是否考虑过采用面向方面的方法,而不是修改每个DAO或服务类?基于AOP的方法可以减少需要编写的代码量。它还将使将来从拦截服务调用切换到拦截DAO调用变得更加容易


无论您将审计代码放在何处,也不管您是在每个服务中手动调用审计还是使用AOP,都有一个非常重要的细节需要注意:确保审计与业务操作发生在同一个数据库事务中。这样,所有内容都可以一起提交或回滚,您就不会有从未发生的事件的审核条目或缺少审核条目的事件。

我在自己的工作中也遇到过类似的设计问题,以下是我目前的理解

您选择的解决方案应基于您的业务规则/问题集

在示例1中,您将
EmployeeRepository
IAuditService
紧密耦合。如果你绝对必须审核每一个员工的变动,这可能是件好事,而忘记这样做可能意味着可怕的后果。如果您不想再使用某种
FakeAuditService
进行审核,任何使用存储库(包括单元测试)的人都需要有意识地选择退出审核

在示例2中,您将
审核服务
作为
员工服务
的职责。这样做,您只会将数据库访问逻辑放在
EmployeeRepository
中,并让
EmployeeService
担心审核问题。这将使
EmployeeRepository
的实现和使用更加简单,并且在使用
EmployeeRepository
时将具有更大的灵活性。但是,如果有人创建了另一个依赖于
IEmployeeRepository
的服务,他们可能会忘记向该服务添加审核逻辑


我个人更喜欢例子2。考虑到单一责任原则,
EmployeeRepository
的责任应该只是简单的数据访问,
EmployeeService
应该包含您关于员工的业务逻辑。此业务逻辑包括审核更改。

我知道您希望对数据库中的所有对象进行审核跟踪,但我并不监督问题的全部复杂性。您的
Employee
EmployeeAuditTable
的外观并不十分清楚,但所选的命名约定表明它包含与Employee表相同的列

审计可以而且经常被认为是一个“交叉关注点”。当您有“所有更改都应该审核”这样的需求时,情况尤其如此。如果审计不是一个业务关注点(或者用例,或者你管它叫什么),你不应该把它放在你的实体、服务或者存储库中;如果只是因为它非常,非常容易忘记在审计中编码,留给您一个不正确的审计跟踪-一些人认为比没有审计跟踪更糟。 从您的代码示例中,我看到您正在使用一个工作单元。我想至少

// commit transaction
提交工作单元跟踪的更改:

// commit transaction
_unitOfWork.Commit();
同样,它提交由工作单元跟踪的更改。这是您的审计挂钩点,不需要在任何服务、实体或存储库中进行编码

事实上,当您使用(N)Hibernate之类的ORM框架时,您可以让ORM为您跟踪更改(它将为此连接到其工作单元中),例如,请参阅或NHibernate的审计框架,也在中讨论。我强烈建议您阅读Envers文档,即使您实现了自己的审计解决方案

// commit transaction
_unitOfWork.Commit();