I need to implement a change log/history for some of the BO's in my application and am wondering about the different approaches I could take.
First, a little background. At this time I have 3 BO's in my application that need change tracking implemented. This may increase as additional features are added to the application in subsequent versions. At a minimum, we want to track object creation, deletion and state changes (the app has a workflow engine that the BO's pass through). We may also want to track changes to key properties in the object. For instance, if a Customer's Address is changed, for tracability and accountability purposes we need to record the change (with the date and the user that made the change).
I know this sounds like a straight-forward audit trail, but I think we are looking for more flexibility than would by typical for an audit trail. I may be wrong, but you set up an audit trail to log specific events on a more global level while here I need to be able to vary what events (changes) are logged and may even have to implement logic to determine if the change warrants logging. For example, we may decide that price changes to a Quote less than $50 do not merit logging but any that are higher do get recorded.
So, my original approach was going to utilize triggers in the SQL Server database that would add the appropriate record into the associated history table. But, I have never had real good luck with triggers in the past and have only been reading reasons why not to go that route - data portability being a huge point. (We may find ourselves switching to Oracle - or some other DB - in the near future.)
Next, I considered hard-coding directly into the Insert, Delete and Update sprocs. But, I don't see an easy way to implement the flexibility I need such as turning on and off what properties we track and changes to the threshold values. Plus, detecting what properties changed would require a lookup and compare routine within the sproc - more overhead than I'm willing to impose.
I then started considering the Observer pattern and having a CustomerAuditor (or some other name) object that listened for changes to the Customer objects and added the appropriate records. To do this, I would have to implement a set of events for the Auditor to trap such as CustomerAdded, CustomerUpdated and CustomerDeleted. Afterall, we don't want to record the changes until they are committed.
Having the Observer listen to a parent collection would be the cleanest way to go, but we won't always have a collection to work with - e.g. when we instantiate the BO directly. So, there's some logistics that have to be worked out as far as getting the Observer and BO "talking" to each other. I figure having a factory to instantiate the object would provide me the easiest way to "attach" the Observer.
Finally, I started thinking that this may be better suited as a part of the BO itself. Much like the AuthorizationRules and ValidationRules properties/collections/operations, I could extend the objects to have an AuditRules collection and associated methods. Every time a property is changed we would use the AuditRules collection to determine if we are to track that property and, if so, add the change to the list of pending changes. Then, when the BO is committed, we would also commit the audit record.
My issues with the last approach, which seems like the best at this point, is that this will work great for property change tracking (which would include state tracking) but I would still need a way to track object creation and deletion - of course, maybe these could simply be rules associated with the IsNew and IsDeleted properties...
In addition, committing the audit record in the same trip to the database as the BO's operation would be preferred, so I need some approach to wrapping the event information into the SQL call. I am thinking it could be done as a string parameter with all of the changes concatenated together, I guess.
In fact, perhaps a combination of approaches would work the best. Since I have separate procedures for the Insert, Delete and Update operations, I could embed the necessary SQL within those procedures to add the necessary audit record without involving the BO. In other words, I would add a record indicating the object was created in the Insert sproc and likewise for the Delete sproc. Then, add the additional parameter to the Update sproc to record the properties that had been changed by the update. But is it wise to mix approaches like this?
I know this is a lot to throw out there and only hope that this instigates a healthy discussion on how some of you have already approach this and the pro's & con's of each.
Thanks in advance.
ajj3085:I would do full row level auditing on the tables your interested in. I know that you don't necessarly want to log everything, but building complex rules to audit only certain pieces of data or only at certain times will just make your development more complex and harder to maintain.
To generate the data, I would use the update, delete and insert procs to copy row state into the audit table (so Person would have a PersonAudit, Phone would have PhoneAudit). Maybe create a central Audit table so that the various xxxAudit tables can be liked to a single transaction.
Instead, I would focus on building business objects that can get the audit details in which the users are interested. Anything else can be filtered by the object automatically and just present only the data requested.
So, you are saying that I should log everything and apply a filter on the viewing end of things? I understand the concept, but isn't the idea of defining audit levels and criteria to reduce the impact logging has on performance, space, etc.?
I guess the knock (at least imo) on triggers has to do with portability of the system. You can obviously use triggers with SQL Server and Oracle, but beyond that there is no guarantee that the approach will work.
Andy, I guess you are right that there is not only a semantical difference between auditing and logging but there really is a functional difference as well. But, I think my definition is backwards from yours.
To me, logging is something that is either enabled or not and is used for diagnostic, maintenance or administrative-type purposes. A good example of what I consider a log is a web site that tracks a visitor as they navigate from page to page. Each page hit is recorded in the log. The same information is recorded each time and the data can be later massaged and manipulated as desired for analysis. It is automatic, consistent and is either on or off.
Auditing is something that is event-driven by nature (as is logging, I guess) but is based on the occurrance of a specific event - or set of conditions - and can be triggered by one or the other boolean state of those conditions. For instance, recording each time there is a failed login attempt in the application. I see auditing as more of a security, accountability, tracability (quality) tool.
Even as I explain it, the lines are blurred. Maybe it is just semantics, I don't know. But I still haven't completely solved my situation yet athough I have implemented a short-term solution to keep my efforts moving forward.
At this point, I am using a BO because I think that may be the direction I take this as I implement a more comprehensive model. The BO is a child of the object being audited and is created in response to a particular event. When the parent is persisted, the audit/log entry is as well. Because the BO is a child object (part of a child collection), I am able to easily retrieve the entries from the data store and display the parent's "history" on the page.
Down the line I am thinking of extracting this from the BO and having a collaborative Auditor object using the Observer pattern. I would "register" the object(s) to be audited which would allow the auditor to "hook into" the object's events, such as PropertyChanged. Somewhere/somehow there'd be a list of what is being audited so the Auditor could filter the events...........
Anyway, my thought process is relatively new and pretty rough. My short-term fix gives me what I need for now, so I suppose that's good enough, eh?
You might want to consider to read this thread http://forums.lhotka.net/forums/thread/10206.aspx.
Although the thread was more on object level audit trail, maybe it could give you an idea.
Thanks for the reference. I'm pretty much on the same page with the exception that we also track "state" changes, meaning an entry when the object itself is created and/or deleted. Other than that, the example for tracking property changes pretty much reflects what I am trying to accomplish. Having started down this path, however, I am leaning towards something a bit more powerful, flexible and... "generic". I haven't rules out the AuditRules idea yet, but I like the Observer or Proxy pattern ideas as well. I would like something that is there if I want it and doesn't consume memory or processor time if not - much like the way ValidationRules are setup. So, we'll see.
Copyright (c) Marimer LLC