我刚刚学习DDD(Eric Evans的书在我面前开放),我遇到了一个我无法找到答案的问题。当你试图获得一个简单的查找记录列表时,你在DDD中做了什么?
实施例
员工ID:123
员工姓名:John Doe
州:阿拉斯加州(下拉列表)
郡:瓦西拉(下拉 - 将根据州进行过滤)。
例如,假设您有一个Employee域对象,一个IEmployeeRepository接口和一个EmployeeRepository类。 UI将使用它来显示员工列表和个人详细信息。在UI中,您希望使用员工所在州和郡的下拉列表。将根据选择的状态筛选可用县。
不幸的是,数据库表和UI看起来非常不同。在tblEmployees中,它包含州代码= AK和县代码= 02130,而不是州和县名。
旧方法(在我开始此DDD任务之前)将非常简单,只需创建2个查询并使用DataReader填充下拉列表。下拉列表中显示的下方是值,它会自动用于表单帖子。
但是,对于DDD,我不确定你应该如何做到这一点。我首先开始创建State和County对象以及存储库的存储库和接口。但是,编写4个类+2个接口以及hbm.xml文件和Employee Business对象中的管道对于2个下拉列表的2个查询来说似乎有些过分。必须有更好的方法,不是吗?我不会很快改变州或县表中的记录,即使我这样做,也不会通过这个应用程序。因此,如果我不需要,我真的不想为State和County创建业务对象。
我看到的最简单的解决方案是使用返回字典的方法创建一个辅助类,例如GetStatesAll(),GetState()和GetCounties()以及GetCounty(),但从DDD的角度来看,这感觉不对。
请帮忙。如何在没有过度设计的情况下使用DDD只进行几次简单的查找?
最终解决方案 我认为我终于通过经验找到了答案,即将GetStates()方法放入自己的Data Access类中,尽管不是存储库类。由于我只进行只读访问,因此我将其放入结构DTO中。由于数据库很小,我把它们完全扔进了一个类,就像下面描述的Todd一样。
我的结论:
答案 0 :(得分:6)
使用DDD我有类似的内容:
interface IAddressService
{
IList<Country> GetCountries ();
IList<State> GetStatesByCountry (string country);
IList<City> GetCitiesByState (string state);
// snip
}
Country,State和City是来自数据库中查找表的值对象。
答案 1 :(得分:6)
您可能希望了解Command Query Separation的概念。我不担心查找值的类型存储库,但我仍然可能使用DTO类型类而不是数据集等...
您可能希望花一些时间阅读Greg Young的博客,从this one开始到现在。他没有专门讨论填充查找数据,但他经常谈到不通过存储库中的类型化方法处理应用程序的读取/报告功能。
答案 2 :(得分:3)
我前段时间阅读了Mathias Verraes的一篇文章,讨论了here。他谈到了将模型中的值对象与为UI服务的概念分离。
当被问及是否将国家/地区建模为实体或价值对象时,引用该文章:
建模国家没有什么本质上的错误 实体并将它们存储在数据库中。但在大多数情况下,那 过度复杂的事情。国家不经常改变。当一个 国家名称的变化,实际上是出于所有实际目的,a 新国家。如果有一天国家不再存在,你就不能 只需更改所有地址,因为可能该国家被拆分 进入两个国家。
他提出了一种不同的方法来引入一种名为AvailableCountry
的新概念:
这些可用的国家/地区可以是数据库中的实体,也可以是a中的记录 JSON,甚至是代码中的硬编码列表。 (这取决于 企业是否希望通过UI轻松访问它们。)
<?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];
}
}
所以似乎有第三种解决方案是将查找表建模为值对象和实体。
BTW请务必查看some serious discussions关于该文章的评论部分。
答案 3 :(得分:2)
州和县不是实体,而是价值对象。它们不是您系统的主题。你说你以前对待这些的方式是可以的。您何时可以根据域模型状态的变化更改数据库中的州或县记录?不,所以这些不需要存储库。
答案 4 :(得分:2)
如果你想学习如何做DDD而不过度复杂,那你就读错了书。 : - )
如果满足您的需求,您提出的最简单的解决方案就很好。在业务对象中封装地址数据可以像应用程序要求一样简单或复杂。例如,State对象与County具有一对多的关系,因此如果您选择以这种方式对其进行建模,那么Employee实际上只需要引用一个County。如果需要,我只会介绍那种复杂性。
此外,我不认为通过为您的存储库定义接口可以获得很多东西,除非您的对象可能有多个存储库。