Search code examples
design-patternsdomain-driven-designrepository-pattern

DDD Auditing? Domain Layer or Repository Layer?


The application I'm currently working on requires every change made to the database be audited in another table:

ex: Employee Table has EmployeeAuditTable

I've been debating on where I should put the auditing functionality! Following DDD, could anyone offer me their advice and opinion?

The options that come to mind to me are the following

  1. When a "Repository" calls save changes, I should perform the auditing logic from the repository (Is this bad design/practice to have the Repository not only persist changes but also persist auditing details as well? Is it even good practice to have a service being called from within a repository (IAuditService in this case))?

Example 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
   }
}
  1. Should I call IAuditService within my application services

Example 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      
   }
}

Solution

  • I understand that you want to have an audit trail for all objects in your database, but I don't oversee the full complexity of your problem. It is not quite clear what your Employee and your EmployeeAuditTable look like, but the naming convention chosen suggests that it contains the same columns as the employee table.

    Auditing can be, and often is, considered a "cross cutting concern". This is especially the case when you have requirements like "all changes should be audited". If auditing is not a business concern (or use case or whatever you call it), you should NOT put it in your entities, services or repositories; if only for the reason that it is very, very easy to forget to code in the audit, leaving you with an incorrect audit trail - which some consider worse than no audit trail at all.

    Form your code example, I see you are using a unit of work. I imagine that at

    // commit transaction
    

    you commit the changes tracked by the unit of work:

    // commit transaction
    _unitOfWork.Commit();
    

    Again, it commits the changes tracked by the unit of work. There is your auditing hook point and it does not need to involve coding in any of your services, entities or repositories.

    In fact, when you're using an ORM framework like (N)Hibernate, you can let the ORM track the changes for you (it will hook into its unit of work for this), see for instance the wiki page "creating an audit log using events" or the Envers auditing framework for NHibernate, also discussed in this answer on SO. I highly recommend reading the Envers docs, even if you implement your own auditing solution.