我正在尝试实施 DDD,感觉我已经掌握了窍门,但我也遇到了一些问题。
在我 90% 的域对象中,我想知道最后一个对其进行更改的用户。我不需要完整的审计跟踪——这对我的需求来说太过分了。
我所有的类都实现了一个抽象基类,其中包含:
public abstract class Base
{
public User LastChangedBy { get; set; }
public DateTime LastChangedDate { get; set; }
}
选项 1:遵循 DDD 原则,但不那么优雅
永远不要让对象进入无效状态
public abstract class Base
{
public User LastChangedBy { get; protected set; }
public DateTime LastChangedDate { get; protected set; }
}
public SomeObject
{
.....
SomeBehaviorThatChangesObject(User changedBy, ...)
AnotherBehaviorThatChangesObject(User changedBy, ...)
}
我必须将我所有的设置器设为私有,将基类设置器设为受保护的。对对象所做的每一次更改都需要通过一个将 (User changedBy) 作为参数的方法来完成。
非常安全,但是由于用户可能会对对象进行 6 次更改,因此我必须为每个更改提供 User 对象。好吧,实际上,我必须为我的领域模型中的几乎所有方法提供这个......
选项2:不是最好的,但我读过它
引入一个 bool IsValid 字段。在所有设置器中,我将 IsValid 设置为 false。创建了一个将此字段设置为 true 的方法 AcceptChanges(User changesAcceptedBy)。请记住在持久化对象之前始终检查对象是否有效。
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;
}
}
选项3:我认为最实用但不是很DDD
在 UnitOfWork 的持久层或 repository.SaveChanges(User changedBy) 等存储库中执行此操作。我,或者任何实现这部分的人,可能会忘记这一点,这会使对象处于无效状态......
public SomeRepository
{
public void Update (User changedBy)
{...}
}
能够看到谁最后更改了实体是很正常的,但我还没有看到任何实现 DDD 的好例子。你怎么做到这一点?
更新 作为对 jgauffins 解决方案的回应:谢谢,Base 实际上实现了一个接口,并且我非常简化,以免信息过载。我觉得这并不优雅,因为每种方法都需要我的用户对象作为每次更改的参数,而且我突然不得不将所有 setter 设为私有...
把它放在回购中可以完成这项工作,但上面的论点是有效的,我没有想到这一点,所以这正是我问的原因。我实际上正在计划一项可能需要更改某些对象的服务。
这是一个很大的变化,改变了数百种方法和属性,所以我想保持舒尔。而且我最终会用很多方法来设置一个字段,就像我在这篇文章中提到的那样,我不需要一个方法,因为“set”描述得足以让用户知道更改做了什么......我的直觉但是说你是对的,只是想看看是否有其他方法可以做同样的事情。
最终更新:
阅读所有评论、问题和建议的解决方案后,我意识到我可能不得不重新考虑如何看待 LastChangedBy 和 LastChangedDate 字段。对于某些对象,这实际上与我认为属于该领域的事物有关。对于许多其他人来说,我正在寻找真正的审计。我没有意识到区别。
即 Document 对象可以由创建者以外的其他人更改,并且会与某些行为(通知创建者等)相关联。在这些情况下,实际上并不是最后一个更改我感兴趣的文档对象的人,而是最后一个编辑文档内容的人。
我没有将它与 LastChangedBy 混合,而是为 LastEditedBy 创建字段。如果我需要跟踪每个更改,这甚至可以是编辑列表。
当然,大部分时间这将是 LastChangedBy 的同一用户,但请查看创建者进入并更改属性以锁定文档以进行进一步编辑的场景。然后文档并没有真正编辑,而是更改了,出于审计原因我想跟踪一些内容。如果我使用相同的字段来跟踪编辑,那就错了。我可以这样做,因为现在的要求是创建者可以进去查看上次更改文档的人,但这并不是真正正确的解决方案。
为了实施审计,我做了以下工作:
首先,我将我的基类划分为需要实现的接口。lastChangedBy 和 LastChangedDate 定义在 IAuditable 接口中,需要审计的对象必须实现。
然后像这样覆盖 DbContext.SaveChanges() :
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();
}
当然还有其他方法可以做到这一点,但这似乎至少是一个开始。
现在,我意识到我的问题本来可以更好,但老实说,我没有意识到问题的一部分是我想跟踪不同对象的不同原因的变化。上面文档的示例只是 LastChangedBy 不是我真正需要在我的域中设置或跟踪的对象之一,但可以将其重写为像上面那样更具体。