【问题标题】:How do public websites built using DDD keep from having one main aggregate root使用 DDD 构建的公共网站如何避免拥有一个主要的聚合根
【发布时间】:2011-08-31 09:20:40
【问题描述】:

我在创建一个可公开访问的网站时尝试使用域驱动设计。我遇到的一个问题是试图弄清楚我的模型的聚合根应该是什么。我很清楚哪些对象是实体对象,哪些对象是值对象。

与大多数公共网站一样,我的网站不允许每个用户查看网站中存储的每条信息。相反,他们只能看到自己拥有的信息。对于我的站点,用户将创建“项目”,他们也可以与其他用户共享。然而,用户仍然只能看到他们创建或受邀加入的项目中的信息。我的模型中的所有其他对象都存在于一个项目中,如果一个项目被删除,它包含的所有对象也应该被删除。

这是否意味着我应该拥有一个主要的“Project”聚合根类型和一个“ProjectRepository”存储库?每次请求我网站上的任何页面时,加载整个项目对我来说似乎效率低下。实际上,这不是什么大问题,因为我使用的是 NHibernate,它只会延迟加载项目中请求的项目。然而,让网站的效率如此依赖于使用延迟加载的 ORM 似乎是糟糕的设计。


这里有一个更新,希望能让我的问题更清楚。

首先,我试图了解我的项目类型是否应该是我的模型的聚合根。项目可以单独存在,而报告必须存在于项目中。如果删除项目,则应删除相应的报告。这是否意味着 Project 可以或应该是聚合根?这个我不是很清楚。

如果 Project 是聚合根,那么 Report 应该不正确?据我了解,根不应嵌套在 DDD 中。此外,只允许从存储库中检索聚合根。因此,如果 Report 不是聚合根目录,那么我不应该有 ReportsRepository,而应该只通过从 ProjectsRepository 检索到的项目访问报告。因此,即使页面只需要来自单个报表的数据,它也需要从 ProjectRepository 加载整个项目才能获取报表。

此外,如果 Project 是包含 Reports 的聚合根,则也可以设置从 ProjectRepository 中删除 Project 以删除它包含的 Reports。但是,如果 Project 和 Report 都是聚合根,那么在删除 Project 时不允许 ProjectRepository 删除 Reports 会打破聚合之间的界限吗?聚合根及其对应的存储库不是应该相互独立吗?

【问题讨论】:

  • "然而,让网站的效率如此依赖于使用延迟加载的 ORM 似乎是糟糕的设计" 为什么?这是扎实的技术。一个基本的 SQL “SELECT...WHERE...”实际上是延迟加载数据子集。它出什么问题了?你能提供更具体和具体的问题吗?
  • 好点,我只是没想到那样。那么,也许有一个单一的“项目”聚合根是有意义的?如果我不使用 NHibernate 调用 ProjectRepository.GetProject(int id) 将是一个相当繁重的过程来构建整个项目对象。几乎每个页面请求都需要调用它。 NHibernate 没有任何问题。我只是认为存储库模式的重点是允许存储库的实现发生变化而不会产生重大影响。
  • @Eric Anastas:“在不产生重大影响的情况下实施存储库进行更改”?我觉得你的水平太高了。你为什么专注于这些 Big Aggregate 对象?为什么项目聚合是问题域中唯一的东西?这不会分解吗?
  • 确实可以分解项目。一个项目将包含问题、报告和许多其他类型的对象,这些对象本身似乎可以是聚合根。然而,这些对象也总是与一个项目相关联,如果它们的父项目被删除,也需要被删除。或者换句话说,报告或问题永远不会单独存在,它将始终与项目相关联。
  • @Eric Anastas:“与项目相关”似乎并不表示您必须为每个页面视图构建整个项目。 “如果他们的父项目被删除,也需要被删除”似乎并不表示您必须为每个页面视图构建整个项目。我不清楚为什么整个项目必须出现在任何给定的页面视图中。此外,我不清楚这与 DDD 有何关系。你能更新这个问题来解释你的推理吗?

标签: nhibernate domain-driven-design aggregate aggregateroot


【解决方案1】:

我认为您的担忧令人困惑。安全性(谁可以查看什么)不是域问题。不要让它污染您的域。如果用户无法查看项目中的报告,请在域模型以外的其他地方强制执行。

此外,我认为您可能会受益于将 ViewModel(用于读取)与域模型(主要用于写入)分离。它们都共享一个数据模型(因为它们从同一个数据库模式中读取),但它们的用途确实不同。

在实践中(在 .NET 中),解耦 ViewModel 将具有以下大部分或全部特征:

  • 您的 ASP.NET MVC 视图绑定到一个表示屏幕上所有数据的类。例如,不仅是 <select> 元素中当前选定的值,还包括代表 <select> 元素中所有可能的 <option> 值的属性。

这些位于 MyProject.ViewModel 命名空间中:

public class ProjectLead // Note that this class is specifically designed for display.  This is not the domain object.
{
    public Guid Id;
    public string Name;
}

public class ProjectViewModel
{
    public Guid ProjectLeadId;
    public IEnumerable<ProjectLead> ProjectLeads;
}

您的 Edit() 控制器操作将调用类似:

new ProjectViewModelRepository.Load(id);

ProjectViewModelRepository 会将您的数据模型(nHibernate 类或 ADO.NET 数据行或其他)映射到 ProjectViewModel 实例。

然后,在 UI 中提交表单调用的控制器动作如下所示:

    public ActionResult Edit(ProjectViewModel viewModel)
    {
        var repo = new ProjectRepository();
        var project = repo.Load(viewModel.Id);
        // Map the properties of the viewmodel to properties/methods of the Project domain class
        repo.Save(project);
    }

这里的大创意是Single Responsibility PrincipleProjectViewModel 表示项目在屏幕上的显示方式。 Project 域类代表业务逻辑以及与项目无关的内容,与数据在屏幕上的显示方式无关。

分离这些东西确实解决了很多问题,因为我将域类用于多种目的:持久性、域逻辑和显示。但是......如果这一切听起来完全是矫枉过正,我会验证您的域是否足够复杂以真正保证 DDD。

【讨论】:

  • 您能否详细说明“解耦您的 ViewModel”。可以举个例子吗?
  • 谢谢,这对我有很大帮助,这正是我最终要做的。
  • 另外,安全问题是一个单独的问题。简短的回答是“在应用程序服务层做”,但这应该是一个单独的问题。如果您仍然需要帮助,请发布新问题并将链接作为评论删除。
猜你喜欢
  • 1970-01-01
  • 2019-08-07
  • 2013-05-31
  • 2018-04-06
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多