I'm trying to implement DDD and feel I get the hang of it, but I have some problems too.
In 90 % of my domain objects I want to know the last user that made changes to it. I do not need a full audit trail - that is overkill for my needs.
All my classes implement an abstract base class containing:
public abstract class Base
{
public User LastChangedBy { get; set; }
public DateTime LastChangedDate { get; set; }
}
Option 1: Follows DDD principles, but not so elegant
Never let the object enter invalid state
public abstract class Base
{
public User LastChangedBy { get; protected set; }
public DateTime LastChangedDate { get; protected set; }
}
public SomeObject
{
.....
SomeBehaviorThatChangesObject(User changedBy, ...)
AnotherBehaviorThatChangesObject(User changedBy, ...)
}
I have to make all my setters private, the Base class setters as proteced. Every change made to objects need to be done through a method that takes (User changedBy) as a parameter.
Very safe, BUT since the user might do 6 changes to the object, i have to supply the User object for each one of them. Well, actually, I have to supply this for pretty much every method in my domain model...
Option 2: not the best one, but I've read about it
Introduce a bool IsValid field. In all setters I set the IsValid to false. Created a method AcceptChanges(User changesAcceptedBy) that sets this field to true. Remember to always check if object is valid before persisting it.
public abstract class Base
{
public bool IsValid {get; protected set;}
public User LastChangedBy { get; protected set; }
public DateTime LastChangedDate { get; protected set; }
}
public SomeObject
{
public Object Propery{get; set{IsValid = false; ...}}
.....
SomeBehaviorThatChangesObject(...)
{
//change the object
IsValid = false;
}
}
Option 3: I my eyes most practical but not very DDD
Do it in the persistance layer in UnitOfWork or in the repositories like repository.SaveChanges(User changedBy). I, or whoever implements this part, might forget that and it would leave the object in an invalid state...
public SomeRepository
{
public void Update (User changedBy)
{...}
}
It's quite normal to be able to see who changed an entity last, but I've not seen any good examples for it implementing DDD. How do you do this?
Update In response to jgauffins solution: Thanks, Base does actually implement an interface, and i simlpified very much to not overload with info. I feel it is not elegant becouse every method will need my user object as parameter for every change, and i suddenly have to make absoulutely all setters private...
Putting it in the repo could do the job, but the argument above is valid and I haven't thought of that so that's exactly why I asked. I'm actually planning a service that might need to change some objects.
It's a big change, changing hundreds of methods and properties so I want to be shure. And also I will end up with quite a few methods just to set one field like I mentioned in this post, where I dont need a method becouse "set" describes enough for the user to know what the change does... My gut feeling however says you're right, just wanted see if there was alterantive ways to do the same thing.
Final update:
Reading all the comments, questions and suggested solutions I realized that I probably had to rethink how I looked at the LastChangedBy and LastChangedDate fields. For some of the objects this actually relates to something that I feel belong to the domain. For many others it's really auditing I'm looking for. I didn't realize the difference.
I.e. the Document object can be changed by other than the creator and that would be assioiated with some behavior (notifying creator etc). In these cases it is not really the person that last changed the document object I'm interested in, but it is the last one who edited the content of the document.
Instead of mixing this with LastChangedBy, I create fields for LastEditedBy. This could even be a list of Editors if I need to track every change.
Of course much of the time this will be the same user as the LastChangedBy, but look at the scenario where the creator goes in and changes a property to lock the document for further editing. Then the document isn't really edited, but changed and something I would like to track for audit reasons. it would be wrong then if I used the same field to track edits. i could do that, since right now the requirement is that the creator can go in and see who changed the document the last time, but it is not really the right solution.
To implement the audit i did the following:
First of all I divided my Base Class into interfaces that needs to be implemented. The lastChangedBy and LastChangedDate is defined in IAuditable interface which objects that needs auditing has to implement.
Then overriding DbContext.SaveChanges() like this:
public override int SaveChanges()
{
if(ChangeTracker.Entries<IAuditable>().Any())
throw new InvalidOperationException
(
"Tried to save changes on an object that is needs to be Audited. Please provide the User that makes the changes!"
);
return base.SaveChanges();
}
public int SaveChanges(User changedBy)
{
var entries = ChangeTracker.Entries<IAuditable>().Where(entry=>entry.State == EntityState.Modified);
foreach (var dbEntityEntry in entries)
{
dbEntityEntry.Entity.Audit(DateTime.Now, changedBy);
}
return base.SaveChanges();
}
There is of course other ways to do this, but this seemed like a start at least.
Now, I realize that my question could have been better, but honestly I did not realize that part of the problem was that I wanted to track changes for different reasons on different objects. The example with the document above is only one of more objects where LastChangedBy is not really what I need to set or track in my domain, but it can be rewritten to be more spesific like above.