C# 工作单元模式如何适应对新聚合的引用?
背景 据我所知,工作单元(UoW)模式本质上提供了事务语义。换句话说,给定由存储库持久化的聚合域,UoW类允许域的使用者将存储库方法的调用注册到原子操作中。假设我们有:C# 工作单元模式如何适应对新聚合的引用?,c#,design-patterns,domain-driven-design,unit-of-work,atomicity,C#,Design Patterns,Domain Driven Design,Unit Of Work,Atomicity,背景 据我所知,工作单元(UoW)模式本质上提供了事务语义。换句话说,给定由存储库持久化的聚合域,UoW类允许域的使用者将存储库方法的调用注册到原子操作中。假设我们有: interface IAggregate<TKey> { TKey Id { get; } } interface IRepository<TEntity, in TKey> where TEntity : IAggregate<TKey> { TEntity Get(TKe
interface IAggregate<TKey> {
TKey Id { get; }
}
interface IRepository<TEntity, in TKey> where TEntity : IAggregate<TKey> {
TEntity Get(TKey id);
void Save(TEntity entity);
void Remove(TEntity entity);
}
interface IUnitOfWork {
void RegisterSave<TEntity>(TEntity entity);
void RegisterRemove<TEntity>(TEntity entity);
void RegisterUnitOfWork(IUnitOfWork uow);
void Commit();
void Rollback();
}
问题
考虑到上述关注点分离和对聚合边界的解释,如何为在事务上需要创建聚合并将其存储库生成的Id保存在另一个聚合中的消费者提供事务支持?例如,当Blog.UserId
设置为User.Id
时,如何以事务方式创建User
和Blog
我已经想出了一些答案(标记为community wiki),但我还是在这里发布我的问题,以征求反馈和更多答案。ID类型作为参考类型是否应该更智能?这样,当存储库更新ID时,另一个聚合可以通过引用来访问它。例如:
class User : IAggregate<int> {
int Id { get; private set; }
}
class Blog : IAggregate<int> {
int Id { get; private set; }
int AuthorUserId { get; set; }
}
interface IKey<TAggregate> : IEquatable<IKey<TAggregate>>
{
/* should provide a ctor that accepts a string */
TAggregate GetAggregateType();
string ToString();
bool IsAssigned { get; }
}
interface IAggregate<TSelf> where TSelf : IAggregate<TSelf>
{
IKey<TSelf> Id { get; }
}
interface IRepository<TAggregate> where TAggregate : IAggregate<TAggregate>
{
TAggregate Get(IKey<TAggregate> id);
void Save(TAggregate entity);
void Remove(TAggregate entity);
}
class User : IAggregate<User>
{
public IKey<User> Id { get; private set; }
}
class Blog : IAggregate<Blog>
{
public IKey<Blog> Id { get; private set; }
public IKey<User> Author { get; private set; }
}
接口IKey:IEquatable
{
/*应该提供一个接受字符串的构造函数*/
TAggregate GetAggregateType();
字符串ToString();
布尔被分配{get;}
}
接口IAggregate,其中TSelf:IAggregate
{
IKey Id{get;}
}
接口i位置,其中TAggregate:iagregate
{
TAggregate-Get(IKey-id);
作废保存(TAggregate实体);
无效删除(TAggregate实体);
}
类用户:IAggregate
{
公共IKey Id{get;private set;}
}
班级博客:IAggregate
{
公共IKey Id{get;private set;}
公共IKey作者{get;private set;}
}
IKey
的实现与IRepository
的实现在同一个包中提供。似乎必须放松以下模式约束之一:
- 存储库负责生成聚合ID。相反,必须使用自然密钥
- UoW无法注册任意
委托。相反,允许UoW注册任意操作
操作
- 聚合间关系被对象化为ID属性。相反,请使用聚合类型属性,例如:
部分类博客:IAggregate{
用户作者{get;set;}
}
那么,我是否错误地解释了这些模式?还是在这方面它们实际上是有限的
考虑到上述关注点分离和对聚合边界的解释,如何为在事务上需要创建聚合并将其存储库生成的Id保存在另一个聚合中的消费者提供事务支持?例如,如何在Blog.UserId设置为User.Id的情况下以事务方式创建用户和博客
事情是这样的——聚合也负责绘制事务边界。这意味着——如果博客创建以某种方式失败,则不需要同时创建用户和博客,也不需要回滚用户创建
如果有这样的需求,那么您就错了
只是发布一些可能有用的快速评论
interface IAggregate<TKey> {
TKey Id { get; }
}
interface IAggregate{
TKey Id{get;}
}
接口应该用于定义行为(角色),而不是定义实现类将包含的内容(在本例中是关于键类型的知识)
谷歌不能足够快地找到正确的解释,为什么它确实是如此,虽然。。。
我稍后再试
interface IRepository<TEntity, in TKey> where TEntity : IAggregate<TKey> {
TEntity Get(TKey id);
void Save(TEntity entity);
void Remove(TEntity entity);
}
接口位置,其中tenty:iagregate{
TEntity-Get(TKey-id);
无效保存(潜在实体);
无效删除(潜在实体);
}
避免使用
接口IUnitOfWork{
无效登记保存(实体);
无效登记删除(实体);
无效注册表工作(IUNITOWORK uow);
无效提交();
无效回滚();
}
避免使用(这个可以解决你的问题)
接口IAggregate,其中TSelf:IAggregate
{
IKey Id{get;}
}
继续学习。很快,您将停止滥用接口和泛型。:) 恕我直言……这个问题和你自己的答案就像你在和自己交谈。为什么提出一个问题然后回答?为什么不创建一个wiki呢?很好。只是查了一下。显然,社区wiki不再可用:。正确的礼仪是什么?尽管我有答案,但我还是提出了这个问题,因为我想知道还有什么其他答案。我应该把答案写在问题里吗?我希望人们能够对他们投票,不是为了让我获得声誉,而是为了让我看到他们是如何被“人群”衡量的。将我自己的答案转移到问题中。回答自己的问题没什么错-见。只要你假装你在上,如果你不想让你的答案有代表性,你可以把你的答案标记为社区维基。请参阅对的第一个答案中的详细信息。它还解释了一个问题是如何获得社区wiki状态的。是的,我觉得聚合边界应该适应事务状态的变化。在聚合根有数百个实体时,我打破了这一点,以提高性能,不过最好实现一个延迟加载代理。无论如何,当使用UoW而不使用DDD时,我提出的一般问题仍然会出现。我不同意必须避免使用通用存储库。尽管您不会对它们进行任何多态性处理(胡说:
foreach(存储库中的var r){var e=r.Get(5);e.Id=rnd.GetNext();r.Save(e);}
),但它们确实起到了解释代码意图的支持作用,特别是当XML文档被添加到接口时。您提供的工作单元链接不能完全避免UoW。在CQR中,一个命令的执行是否会导致多个事件发生,而所有这些事件都应该以原子方式持久化?@gWiz Genericness模糊了意图。通用存储库的优势在于
interface IRepository<TEntity, in TKey> where TEntity : IAggregate<TKey> {
TEntity Get(TKey id);
void Save(TEntity entity);
void Remove(TEntity entity);
}
interface IUnitOfWork {
void RegisterSave<TEntity>(TEntity entity);
void RegisterRemove<TEntity>(TEntity entity);
void RegisterUnitOfWork(IUnitOfWork uow);
void Commit();
void Rollback();
}
interface IAggregate<TSelf> where TSelf : IAggregate<TSelf>
{
IKey<TSelf> Id { get; }
}