3

背景

据我了解,工作单元 (UoW) 模式本质上提供了事务语义。换句话说,给定一个由存储库持久化的聚合域,UoW 类允许域的消费者将存储库方法的调用注册到原子操作中。假设我们有:

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();
}

假设IRepository使用关系数据库的实现,并且IUnitOfWork.Commit仅与数据库建立事务并继续为已注册的所有操作调用SaveRemove在适当的实例上进行。IRepository我想说我上面概述的是对聚合根、存储库和 UoW 模式的标准、直接解释(尽管 NHibernate/EF 和所有它们臃肿的荣耀)。

过去,我将聚合根边界的概念解释为,从一个聚合到另一个聚合的引用应该由源聚合上目标聚合的 Id 属性客观化。例如:

class User : IAggregate<int> {
  int Id { get; private set; }
}

class Blog : IAggregate<int> {
  int Id { get; private set; }
  int AuthorUserId { get; set; }
}

问题

鉴于上述关注点的分离和聚合边界的解释,如何为需要在事务上创建聚合并将其存储库生成的 Id 保存在另一个聚合中的消费者提供事务支持?例如,我怎样才能创建一个User和一个Blog事务性Blog.UserId设置为User.Id

我想出了一些答案(标记为社区 wiki),但无论如何我都会在这里发布我的问题以征求反馈和更多答案。

4

3 回答 3

1

鉴于上述关注点的分离和聚合边界的解释,如何为需要在事务上创建聚合并将其存储库生成的 Id 保存在另一个聚合中的消费者提供事务支持?例如,如何在将 Blog.UserId 设置为 User.Id 的情况下以事务方式创建用户和博客?

事情是 - 聚合也负责绘制事务边界。这意味着 - 如果博客创建以某种方式失败,则不需要同时创建用户和博客并回滚用户创建。

如果有这样的需要 - 你建模聚合错误。


只是发布一些可能有用的快速评论......

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();
}

避免使用工作单元(这个解决了你的问题)。

interface IAggregate<TSelf> where TSelf : IAggregate<TSelf>
{
    IKey<TSelf> Id { get; }
}

保持学习。很快你就会停止滥用接口和泛型。:)

于 2011-01-23T03:48:47.717 回答
0

ID 类型作为引用类型是否应该更智能?这样,当存储库更新 ID 时,可以通过另一个聚合的引用来访问它。例如:

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<TAggregate>在同一个包中提供IRepository<TAggregate>

于 2011-01-20T04:18:53.940 回答
0

似乎必须放松以下模式约束之一:

  • Repository 负责生成聚合 ID。相反,必须使用自然键。
  • UoW 不能注册任意Action代表。相反,允许 UoW 注册任意Actions.
  • 聚合之间的关系被具体化为 ID 属性。相反,使用聚合类型的属性,例如:

 

partial class Blog : IAggregate<int> { 
  User Author { get; set; } 
}

那么我是否错误地解释了这些模式?还是他们实际上在这方面受到限制?

于 2011-01-20T04:20:02.223 回答