【问题标题】:Drop-down list from Domain Model / Repository域模型/存储库的下拉列表
【发布时间】:2013-06-16 16:51:12
【问题描述】:

我希望在我的系统中轻松创建从数据库填充的下拉菜单(例如,用户组列表)。对于这个系统,我通常遵循领域驱动的设计方法,包括稍微适应版本的存储库模式。 (系统是PHP的。)

由于检索给定域对象类的下拉列表是一种常见操作,我想知道在相关存储库上创建 getDropDownList() 方法是否合适。

例如,假设有问题的域对象称为“类别”。我的提议是创建一个CategoryRepository::getDropDownList() 方法,该方法将返回一个类别ID 和标题的关联数组,准备好用于创建HTML <select> 列表。

在过去的项目中,当我在类似存储库的类上创建 getDropDownList() 方法时,其他开发人员之一说这样的方法不属于该类,说它与视图有关比模型。但我不这么认为,因为该方法的目的只是返回列表的原始数据。它甚至不需要用于创建下拉列表;它可以转换为 JSON 数据或任何其他数量的东西。

我的主要问题是:

  1. 像我描述的那样的getDropDownList() 方法是否属于存储库类?如果没有,它应该去哪里?
  2. 这可能只是命名问题吗?如果我将其称为 getSimpleList()getArrayForList() 以表明它返回的是一个数组,而不是已经呈现的 HTML,也许会更好?

继续分类示例,从该方法返回的数据将返回一个关联数组,其中类别 ID 作为键,类别名称作为值,例如:

array(
    1 => 'Category A',
    2 => 'Category B',
    ...
)

【问题讨论】:

  • 我会选择选项 2,呈现下拉菜单是视图的工作
  • @Orangepill,只是好奇,你会给它取什么名字?我刚刚想出了getArrayForList(),这是我目前倾向于的。
  • 或者类似 getNames

标签: php architecture domain-driven-design domain-model


【解决方案1】:

您应该尽最大努力查询您的域。您的域应该专注于完整的聚合/实体。

而是创建一个单独的查询层,专注于使用一些不可知的命名返回数据。

例如,在 C# 中我会有这样的东西:

public interface ICategoryQuery
{
    DataTable All();
}

All 之类的方法通常不会在 CategoryRepository 上找到,因为该域涉及操作数据(命令端)。因此,如果我们经常需要对我们的所有类别执行某些操作以保证使用All 方法,那么我们可能存在设计缺陷。想一想,这可能表明我们正在查询我们的域:)

【讨论】:

  • 您说的是 CQRS 而不是传统的 DDD? Fowler 和 Evans 都将查找器/查询方法描述为存储库的主要特征之一。但是,假设我按照您的建议有单独的查询类;它仍然给我留下同样的问题 - getDropDownListgetArrayForSelectList 方法是否属于这样的 Query 类?该方法不能只调用all,因为我需要指明只会从数据库中选择ID和标题字段; all 在我看来,它会返回一个完整对象的列表。
  • CQRS 并不是真正的 DDD 形式 :) --- 但它是 CQRS,它可以根据您的喜好简单或复杂地应用。事物的查询方面根本不关心域对象。如果你真的需要一个读取模型 (DTO),你可以走这条路。只是很多映射,但这取决于它给你买了什么。您可以随意调用您的方法并返回您喜欢的任何内容。如果您需要一个更具描述性的名称,那就这样吧,但我不会有任何特定于 UI 的位,所以没有“DropDown”或“Select”。但这只是我:)
  • 谢谢...CQRS vs DDD 是一个单独的讨论,但听起来你是说将方法放在某种查询对象上而不是其他地方确实有意义。现在我只需要决定如何称呼它......也许是“getIdsAndNames”?如果没有“下拉”或“选择”一词,我不确定如何命名它。
  • 新想法:我想我可能会创建一个名为 getKeyValueArray(keyField, valueField) 的方法;用法示例:getKeyValueArray('id', 'name')
  • 这是一个选项。无论如何,一种方法是使用底层结果集来提取数据。看看它是如何为你工作的。虽然它看起来确实非常通用,但也许这是值得追求的东西。
【解决方案2】:

恕我直言,您应该为每个程序元素寻找商业意义。视图层只是为了呈现业务规则/数据而存在,应该很容易替换。另一方面,您的存储库是业务模型的一部分,并且绝对应该遵循业务命名(业务人员可以理解的名称)。因此,您建议的方法命名无效。 “DropDownList”、“SimpleList”和“ArrayForList”对业务负责人没有任何意义。

我建议如下:

  • 按书路径(如果性能不是问题)将是方法 CategoryRepository::findAll()/getAll(),它以 Category 实例的形式返回所有类别 - 这样您就可以处理严格的业务元素所有层都非常好,因为您没有引入任何中间类型。在视图层中,您可以轻松地将这些实例格式化为 <option/> 元素
  • 自定义方法(如您所建议的),但名称可被业务人员理解 - 例如getTitlesOfAllCategories() (@return string[] 类别 ID 数组 => 标题)

getDropDownList() 的另一个问题是,由于命名问题,它不能轻易“回收”——想象一下突然需要在<ul><li> 列表中列出类别——是时候用 getBulletedList() 复制你的原始方法了吗? ?:) 复选框呢——也许是 getCheckboxList()?但是,意思总是一样的,你只是想呈现... ta-daaaam... 所有类别

【讨论】:

  • 关于“业务意义”的好点...我对您的“按书本”路径的犹豫是,在 PHP 的每个请求生命周期中,我会不必要地检索一堆字段,但担心这一点可能为时过早的优化(而且总是有缓存)。 getTitlesOfAllCategories 不太准确(尽管可能已经足够接近),因为我也在检索 ID - 我在应该返回哪些数据的问题的末尾添加了一个示例。
  • 我猜你会阻止我上面评论中提到的getKeyValueArray() 想法,因为它没有商业意义?我想如果我在视图层中创建一个方法来构建接受键和值字段名称的下拉列表,我可以使用 findAll/getAll 方法完成类似的操作,例如:$categories = $categoryRepository->getAll(); createDropDownList($categories, 'id', 'name', ...[other options]...);
  • 关于按书本方法,是的 - 这将是缓存的问题(例如,Doctrine 2 自动处理它,只需将其投入足够的 RAM)。缺少 ID 问题是我的错字,我真的想写“@return string[] Array of ID => title”
  • 关于您的第二条评论,这将是很好的视图助手,“id”和“name”可以在内部解析为 getId() 和 getName() 以避免公共实体属性。
  • 谢谢,您的回答很有帮助。
猜你喜欢
  • 2013-04-09
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多