假设我想创建一个博客应用程序,这两个简单的持久化类与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中受益。使用更透明的持久性实现创建域对象的其他(具体!)选项是什么?
答案 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的一些简单封装。