1

我对如何设计聚合有疑问。

我有,CompanyCity实体。这些中的每一个都需要是其自身聚合的聚合根。和实体在整个系统中使用,并且被许多其他实体引用,因此它们不是值对象,也需要在许多不同的场景中访问。所以他们应该有存储库。A将具有诸如, , , ,之类的方法。ProvinceCountryCityProvinceCountryCityRepositoryFindById(int)GetAll()GetByProvince(Province)GetByCountry(Country)GetByName(string)

举个例子。一个Company实体与 a 相关联City,它属于 a ,Province它属于 a Country

聚合根

现在假设我们有一个公司列表页面,其中列出了一些公司及其城市、省和国家。

按 ID 引用

如果一个实体需要引用 a或City,他们将通过 ID 这样做(如 Vaughn Vernon 所建议)。 ProvinceCountry

为了从存储库中获取这些数据,我们需要调用 4 个不同的存储库,然后匹配数据以填充视图。

var companies = CompanyRepository.GetBySomeCriteria();
var cities = CityRepository.GetByIds(companies.Select(x => x.CityId);
var provinces = ProvinceRepository.GetByIds(cities.Select(x => x.ProvinceId);
var countries = CountryRepository.GetByIds(province.Select(x => x.CountryId);

foreach(var company in companies)
{
    var city = cities.Single(x => x.CityId == company.CityId);
    var province = provinces.Single(x => x.ProvinceId == city.ProvinceId);
    var country = countries.Single(x => x.CountryId == province.CountryId);

    someViewModel = new CompanyLineViewModel(company.Name, city.Name, province.Name, country.Name);
}

这是一种非常笨重且效率低下的方法,但显然是“正确”的方式?

引用引用

如果通过引用引用实体,则相同的查询将如下所示:

var companies = CompanyRepository.GetBySomeCriteria();
someViewModel = new CompanyLineViewModel(company.Name, company.City.Name, company.Province.Name, company.Country.Name);

但据我了解,这些实体不能通过引用来引用,因为它们存在于不同的聚合中。

问题

我还能如何更好地设计这些聚合?

即使它们存在于不同的聚合中,我是否可以使用城市模型加载公司实体?我想这将很快打破聚合之间的界限。在更新聚合时处理事务一致性时也会造成混乱。

4

2 回答 2

4

您可以创建一个完全不同的对象(这将只是一个平面数据结构)来表示视图模型并且可以直接从数据库中检索。谷歌“薄读层”或“CQRS”。

于 2014-01-31T13:19:37.113 回答
2

Dennis Traub 已经指出了您可以采取哪些措施来提高查询性能。这种方法查询效率更高,但体积也更大,因为您现在需要额外的代码来使视图模型与聚合保持同步。

如果您不喜欢这种方法或由于其他原因不能使用它,我认为您建议的第一种方法并不比使用直接对象引用更无效或更笨重。假设您在聚合中使用直接对象引用。您将如何将这些聚合保存到持久存储中?当您使用数据库时,会想到以下选项:

  • 如果您正在使用非规范化表Company(例如,使用 MongoDB 等文档数据库),那么您已经有效地优化了视图查询。但是,您需要进行所有额外的工作才能使您的表格与,保持Company同步。高效,但体积庞大,您可能会考虑改为保留真实视图模型(每个用例一个)。CityProvince
  • 如果您将规范化表与关系数据库一起使用,您将使用表中的外键通过它们的 idCompany来引用相应City的等。Province当查询 aCompany时,为了检索填充视图模型所需的字段CityProvince,您可以使用 JOIN 超过 4 个以上的表,或者使用 4 个独立的查询到City, Province, ... 表(例如,当对外键引用使用延迟加载时)。
  • 如果您在非关系数据库中使用规范化表,通常人们会完全按照您建议的代码使用应用程序端连接。对于某些数据库,ORM 工具(例如 Morphia 或 Datanucleus)可以为您节省一些编程工作,但在后台,独立查询仍然存在。

因此,在第 2 和第 3 选项中,如果您让 ORM 解决方案为您生成数据库映射,您可以节省一些琐碎的编程工作,但您的效率并没有得到太大的提高。(JOINs 可以通过适当的索引进行优化,但正确完成这项工作并非易事)。

但是,我想指出的是,当您通过 Id 引用并使用您建议的代码中的程序化应用程序端连接时,您仍然可以完全控制视图模型对象构造和数据库查询。尤其是城市、省等的名称通常很少变化,而且很少,很容易记住。因此,您可以为数据库查询广泛使用内存缓存——甚至可以使用在应用程序启动时从平面文件填充的内存存储库。正确完成后,要为 构建视图模型Company,只需要对表进行一次数据库调用Company,而其他字段则从内存缓存/存储库中检索,我认为这非常有效。

于 2014-02-02T11:33:57.627 回答