在域驱动devise(DDD)中对查找表build模的实用方法是什么?

我刚刚学习DDD(埃里克埃文斯书是在我面前打开),我遇到了一个问题,我无法find答案。 当你只是想得到一个简单的查询logging列表,你在DDD做什么?

防爆。

雇员ID:123
雇员姓名:John Doe
状态:阿拉斯加(下拉式)
县:瓦西拉(下拉式 – 将根据状态过滤)。

例如,假设您有一个Employee域对象,一个IEmployeeRepository接口和一个EmployeeRepository类。 这将被UI用来显示雇员和个人详细信息的列表。 在用户界面中,您希望使用员工居住的州和县的下拉菜单。 可用县将根据select哪个州进行筛选。

不幸的是,数据库表和UI看起来很不一样。 在tblEmployees,它包含州代码= AK和县代码= 02130,而不是州和县的名称。

旧的方式(在我开始这个DDD任务之前)将是非常简单的,只需创build2个查询并使用DataReader来填充下拉菜单。 在下拉列表中显示的值是自动在表单post中使用的值。

不过,DDD,我不知道你应该如何做到这一点。 我首先创build了State和County对象以及存储库的存储库和接口。 但是,编写4个类+ 2个接口,并在hbm.xml文件+员工业务对象的pipe道似乎只是2个下拉的查询矫枉过正。 必须有更好的方法,不是吗? 我不会很快改变州或县表中的logging,即使我这样做了,也不会通过这个应用程序。 所以如果我不需要的话,我真的不想为州和县创build业务对象。

我所看到的最简单的解决scheme就是创build一个帮助类,它带有返回字典的方法,比如GetStatesAll(),GetState()和GetCounties()和GetCounty(),但是从DDD的angular度来看,这只是一种错误。

请帮忙。 如何在没有过度工程的情况下使用DDD?

最终的解决scheme我认为我最终通过经验find了答案,即将GetStates()方法放入其自己的数据访问类中,但不是存储库类。 由于我只是做只读访问,所以我把它扔到一个struct DTO中。 由于数据库很小,我把它们全部扔进一个类,就像下面描述的Todd一样。

我的结论是:

  1. 查找表是永远不值对象,因为查找表总是有一个身份。 如果他们没有身份certificate,你会有重复,这是没有道理的。
  2. 只读查找表可以有一个存储库,但可能不需要一个。 存储库的目标是通过仅通过聚合强制访问来降低复杂性。 通过汇总提供了一种确保业务规则可以执行的方法,例如,如果没有汽车,则不添加轮胎。
  3. 如果允许在查找表上进行CRUD维护,则查找表有自己的存储库是有意义的。
  4. 我最终将代码存储为结构的事实不会使它们成为“值types”。 福勒说,在POEAA中,一个结构是一个值types。 这是真的,结构是不可改变的,这就是为什么福勒说,他们是“价值types”,但我用不同的方式。 我正在使用结构作为一种轻量级的方式来传递DTO,这是我在创build之后从未打算改变的。 事实上,我所使用的结构确实具有身份,但由于它们是只读的,所以它们起到了结构的作用。
  5. 我一直在使用的一种模式,我没有在其他地方看到很多,主要关键字段是不可变的。 它们由构造函数设置,但是它们是只读的(不是私有访问器),一旦创build对象就不能更改。

您可能想查看Command Query Separation的概念。 我不会担心查找值types的存储库,但我仍然可能使用数据集等DTOtypes的类…

您可能需要花一些时间阅读Greg Young的博客, 从这一篇开始到现在。 他并没有专门讨论如何填充查找数据,但他经常谈到不通过存储库上的types化方法来处理应用程序的读取/报告function。

使用DDD我有以下类似的东西:

interface IAddressService { IList<Country> GetCountries (); IList<State> GetStatesByCountry (string country); IList<City> GetCitiesByState (string state); // snip } 

国家,州和城市是来自数据库查找表的价值对象。

州和县不是实体,而是价值对象。 他们不是你的系统的主题。 你说的你以前对待这些方法是可以的。 根据域模型状态的变化,何时更改数据库中的州或县logging? 不,所以这些将不需要存储库。

如果你想学习如何做DDD而不过于复杂,你正在阅读错误的书。 🙂

你提出的最简单的解决scheme是很好的,如果它满足您的需求。 在业务对象中封装地址数据可以像应用程序要求一样简单或复杂。 例如,州对象与县有一对多的关系,所以员工真的只需要引用一个县,如果你select这样的模式。 如果需要,我只会介绍这种复杂性。

另外,我不认为通过为存储库定义接口可以获得很多收益,除非您有可能为您的对象创build多个存储库。

那么我前段时间看过Mathias Verraes的一篇文章。 他谈到了将模型中的值对象与为UI提供服务的概念分离。

当被问及是否将国家作为实体或价值对象的模型时,从文章中引用:

将国家build模为实体并将其存储在数据库中没有任何内在的错误。 但是在大多数情况下,那些过于复杂的事情。 国家不经常改变。 当一个国家的名字发生变化时,实际上就是一个新的国家。 如果一个国家有一天不存在了,你不能简单地改变所有的地址,因为这个国家可能分裂成两个国家。

他build议采用不同的方法来引入一个名为“ AvailableCountry的新概念:

这些可用的国家可以是数据库中的实体,JSON中的logging,甚至可以是代码中的硬编码列表。 (这取决于企业是否希望通过用户界面轻松访问它们。)

 <?php final class Country { private $countryCode; public function __construct($countryCode) { $this->countryCode = $countryCode; } public function __toString() { return $this->countryCode; } } final class AvailableCountry { private $country; private $name; public function __construct(Country $country, $name) { $this->country = $country; $this->name = $name; } /** @return Country */ public function getCountry() { return $this->country; } public function getName() { return $this->name; } } final class AvailableCountryRepository { /** @return AvailableCountry[] */ public function findAll() { return [ 'BE' => new AvailableCountry(new Country('BE'), 'Belgium'), 'FR' => new AvailableCountry(new Country('FR'), 'France'), //... ]; } /** @return AvailableCountry */ public function findByCountry(Country $country) { return $this->findAll()[(string) $country]; } } 

所以看起来有一个第三个解决scheme是将查找表build模为值对象和实体。

顺便说一句,确保你检查评论部分的一些关于这篇文章的认真讨论 。