管理DDD中的持久性

时间:2012-12-04 22:26:22

标签: c# .net architecture domain-driven-design persistence

假设我想创建一个博客应用程序,这两个简单的持久化类与EF Code First或NHibernate一起使用,并从存储库层返回:

public class PostPersistence
{
   public int Id { get; set; }
   public string Text { get; set; }
   public IList<LikePersistence> Likes { get; set; }
}

public class LikePersistence
{
    public int Id { get; set; }
    //... some other properties
}

我无法弄清楚将持久性模型映射到域模型的简洁方法。我希望我的Post域模型界面看起来像这样:

public interface IPost
{
   int Id { get; }
   string Text { get; set; }
   public IEnumerable<ILike> Likes { get; }
   void Like();
}

现在下面的实现如何?也许是这样的:

public class Post : IPost
{
   private readonly PostPersistence _postPersistence;
   private readonly INotificationService _notificationService;

   public int Id 
   { 
       get { return _postPersistence.Id }
   }

   public string Text 
   { 
       get { return _postPersistence.Text; }
       set { _postPersistence.Text = value; }
   }

   public IEnumerable<ILike> Likes
   {
       //this seems really out of place
       return _postPersistence.Likes.Select(likePersistence => new Like(likePersistence ));
   }

   public Post(PostPersistence postPersistence, INotificationService notificationService)
   {
       _postPersistence = postPersistence;
       _notificationService = notificationService;
   }

   public void Like()
   {
       _postPersistence.Likes.Add(new LikePersistence());
       _notificationService.NotifyPostLiked(Id);
   }
}

我花了一些时间阅读有关DDD的内容,但大多数示例都是理论上的或在域层中使用相同的ORM类。我的解决方案似乎非常难看,因为实际上域模型只是ORM类的包装器,它似乎不是一种以域为中心的方法。另外,IEnumerable<ILike> Likes的实现方式让我感到困扰,因为它不会从LINQ to SQL中受益。使用更透明的持久性实现创建域对象的其他(具体!)选项是什么?

2 个答案:

答案 0 :(得分:2)

DDD持久性的目标之一是persistence ignorance,这在某种程度上似乎是在努力。我在您的代码示例中看到的一个问题是,您的实体实现了接口并引用了存储库和服务。在DDD中,实体不应实现仅仅是自身抽象的接口,并且对存储库或服务具有实例依赖性。如果实体上的特定行为需要服务,请将该服务直接传递到相应的方法中。否则,与服务和存储库的所有交互都应该在实体之外完成;通常在应用程序服务中。应用程序服务在存储库和服务之间进行编排,以便调用域实体上的行为。因此,实体不需要直接引用服务或存储库 - 它们只有一些状态和行为可以修改该状态并保持其完整性。然后,ORM的工作是将此状态映射到关系数据库中的表。诸如NHibernate之类的ORM允许您获得相对较大程度的persistence ignorance

<强>更新

  

我仍然不希望用INotificationService公开方法   参数,因为这个服务应该是内部的,上面的层不要   需要了解它。

Post课程的当前实施中,INotificationService与课程具有相同或更高的知名度。如果INotificationService在基础架构层中实现,则必须具有足够的可见性。请查看hexagonal architecture,了解现代架构中的分层概述。

作为旁注,与通知相关联的功能通常可以放入domain events的处理程序中。这是一种实现很大程度去耦的强大技术。

  

使用单独的DTO和域类,您将如何解决?   域对象不知道时的持久性同步问题   关于其潜在的DTO?如何跟踪变化?

DTO和相应的域类存在的原因各不相同。 DTO的目的是跨系统边界传输数据。 DTO与域对象不是一一对应的 - 它们可以表示域对象的一部分或域对象的更改。跟踪更改的一种方法是让DTO明确其包含的更改。例如,假设您有一个允许编辑Post的UI屏幕。该屏幕可以捕获所做的所有更改,并将命令(DTO)中的更改发送到服务。该服务将加载适当的Post实体并应用命令指定的更改。

答案 1 :(得分:0)

我认为你需要做更多的研究,看看所有选项并决定是否真的值得为完整的DDD实施而烦恼,我过去几天一直在那里,所以我会告诉你我的经验。

EF代码首先是非常有前途的,但它有很多问题,我在这里有一个条目 Entity Framework and Domain Driven Design。使用EF,您的域模型可以由EF保留,而无需创建单独的“持久性”类。您可以使用POCO(普通旧对象)并启动并运行一个简单的应用程序,但正如我所说,它还没有完全成熟。

如果使用LINQ to SQL,那么最常见的方法是手动将“数据传输对象”映射到业务对象。对于大型应用程序,手动执行此操作非常困难,因此请检查Automapper等工具。或者,您只需将DTO包装在业务对象中,如

  public class Post
   {
      PostPersistence Post { get; set;}
      public IList<LikePersistence> Likes { get; set; }
       .....
   }

NHibernate:不确定,很长时间没用过它。

我对此的感觉(这只是一种意见,我可能是错的)是你总是要做出妥协,而你却找不到完美的解决方案。如果你给EF几年,它可能会到达那里。我认为将DTO映射到DDD对象的方法可能是最灵活的,因此寻找自动化工具可能值得您花时间。如果你想保持简单,我最喜欢的是在需要时围绕DTO的一些简单封装。