Open/Closed Explained with Auditing Example

SOLID OOP Wednesday, May 21, 2014

Previously, in the Single Responsibility principle, we discussed that whenever you have pieces of code that are doing different things, we should separate those responsibilities normally into different classes. In the example I mentioned (benefit rules), that also paves the way to create more functionality without fooling around near code related to other rules. So we also respected the Open/Closed principle in there too. I'd say that, in that case, we added functionality for horizontally (as new rules are side-by-side and have the same degree of importance - note, in code).

There may be other cases in which you may need to add functionality vertically like add auditing functionality to a repository. Let's say we have a requirement that says every read/write access to a specific table should be audited (username and datetime logged somewhere).

public class SensitiveInformationRepository : ISensitiveInformationRepository
  Information GetInformation(id)
    return context.Information.Single(i => i.Id == id);
  // Other methods

How to implement auditing here? Would you write some code at the beginning and at the top of the method?

If we were to do that here, we would be violating the open/close principle which by definition says that we should be extending functionality instead of changing.

OK then, how do we extend the functionality without changing the contents of the method?

A colleague of mine liked to mention the analogy of putting a coat instead of operating. I think it's a good one. It works by creating another implementation of the ISensitiveInformationRepository which calls the original implementation and contains the new auditing required functionality. An example below:

public class AuditedSensitiveInformationRepository : ISensitiveInformationRepository

Now, do we really have to a create a new "coat" every time we need something new? Like many would say: It depends. Like in many things in like, think about it and use your good judgement. I think sometimes, it's not worthy.

For example, if your controller action needs to set another value to a new property in the view model, would it make sense to create a code for this. The responsibility being the same, creating a "coat" here would just make the code look weirder and more difficult to read.

The next principle is the Liskov substitution one.