Domain driven design 当仍然存在无用事件或撤消的可能性时,是否立即将事件应用于域模型?
我看到的每一个事件来源的例子都是针对网络的。在MVC体系结构中,客户端的视图没有运行域代码,交互性受到限制。我不完全确定如何推断富桌面应用程序,用户可能正在编辑列表或执行其他长时间运行的任务 域模型与持久性无关,与表示无关,只能通过将域事件应用于聚合根来进行变异。我的具体问题是当用户进行未提交的更改时,表示代码是否应该改变域模型?Domain driven design 当仍然存在无用事件或撤消的可能性时,是否立即将事件应用于域模型?,domain-driven-design,desktop-application,event-sourcing,Domain Driven Design,Desktop Application,Event Sourcing,我看到的每一个事件来源的例子都是针对网络的。在MVC体系结构中,客户端的视图没有运行域代码,交互性受到限制。我不完全确定如何推断富桌面应用程序,用户可能正在编辑列表或执行其他长时间运行的任务 域模型与持久性无关,与表示无关,只能通过将域事件应用于聚合根来进行变异。我的具体问题是当用户进行未提交的更改时,表示代码是否应该改变域模型? 如果表示代码没有改变域模型,您如何实施域逻辑?当用户进行编辑时,立即进行域验证和域计算,并将其冒泡到表示模型中,这将是一件好事。否则,必须在视图模型中复制非平凡逻辑
有一个复杂的协作域逻辑,这让我认为DDD/ES是一条出路。我需要一张富客户端视图模型和域模型如何交互的图片,我希望简单和优雅 虽然让存储库管理事务,但我最终还是做了类似的事情 基本上我的存储库都实现了
public interface IEntityRepository<TEntityType, TEventType>{
TEntityType ApplyEvents(IEnumerable<TEventType> events);
Task Commit();
Task Cancel();
}
公共接口客户端存储库{
TEntityType ApplyEvents(IEnumerable事件);
任务提交();
任务取消();
}
因此,虽然ApplyEvents将更新并返回实体,但我也在内部保留起始版本,直到调用Commit。如果调用了Cancel,我只是将它们调回,丢弃事件
其中一个非常好的特性是,一旦事务完成,我只将事件推送到web、DB等。存储库的实现将依赖于数据库或web服务,但调用代码需要知道的只是提交或取消
编辑
要执行取消操作,请将旧实体、更新实体和事件存储在内存结构中。差不多
public class EntityTransaction<TEntityType, TEventType>
{
public TEntityType oldVersion{get;set;}
public TEntityType newVersion{get;set;}
public List<TEventType> events{get;set;}
}
公共类EntityTransaction
{
公共TEntityType oldVersion{get;set;}
公共TEntityType新版本{get;set;}
公共列表事件{get;set;}
}
那么你的ApplyEvents对于一个用户来说
private Dictionary<Guid, EntityTransaction<IUser, IUserEvents>> transactions;
public IUser ApplyEvents(IEnumerable<IUserEvent> events)
{
//get the id somehow
var id = GetUserID(events);
if(transactions.ContainsKey(id) == false){
var user = GetByID(id);
transactions.Add(id, new EntityTransaction{
oldVersion = user;
newVersion = user;
events = new List<IUserEvent>()
});
}
var transaction = transactions[id];
foreach(var ev in events){
transaction.newVersion.When(ev);
transaction.events.Add(ev);
}
}
私有字典事务;
公共IUser应用程序事件(IEnumerable事件)
{
//不知怎么弄到身份证了
var id=GetUserID(事件);
if(transactions.ContainsKey(id)==false){
var user=GetByID(id);
事务。添加(id,新实体事务{
oldVersion=用户;
新版本=用户;
事件=新列表()
});
}
var事务=事务[id];
foreach(事件中的var ev){
交易。新版本。何时(ev);
事务.events.Add(ev);
}
}
然后在“取消”中,如果要取消事务,只需用旧版本替换新版本
有意义吗?我看不出桌面DDD应用程序与MVC应用程序有多大的不同,基本上可以有相同的层,只是它们大部分不是网络分离的 CQRS/ES应用程序在您发出反映用户意图的命令的情况下工作得最好。但我们所说的任务并不是指用户可以在屏幕上采取的每一个行动,它必须在领域中具有意义和目的。正如您在3中正确指出的,无需将每个微修改建模为一个完整的DDD命令和相关事件。它可能会污染您的事件流 所以你基本上有两个层次: UI级操作 这些可以完全在表示层中进行管理。它们叠加起来最终映射到单个命令,但您可以很容易地逐个撤消它们。没有什么可以阻止您将它们建模为封装的微事件。我从来没有在任何UI中看到过“cherrypickable”撤销,也没有真正看到这一点,但只要操作是可交换的(它们的效果不依赖于执行顺序),这应该是可行的,用户可以理解的 域级任务 由命令和相应事件表示的粗粒度活动。如果需要撤消这些操作,我宁愿向事件流附加一个新的回滚事件,也不愿尝试删除一个现有事件(“不要更改过去”) 在UI中反映域不变量和计算 这是您真正需要正确区分这两种任务的地方,因为UI操作通常不会更新屏幕上的任何内容,只会更新一些任务