DDD: Aggregate design - Ссылка между агрегатами

У меня проблема с проектированием агрегатов.

У меня есть сущности Company, City, Province и Country. Каждый из них должен быть совокупным корнем своего собственного агрегата. Сущности City, Province и Country используются во всей системе и на них ссылаются многие другие сущности, поэтому они не являются объектами значений и к ним также необходимо обращаться во многих различных сценариях. Значит, у них должны быть репозитории. CityRepository будет иметь такие методы, как FindById(int), GetAll(), GetByProvince(Province), GetByCountry(Country), GetByName(string).

Возьмем следующий пример. Сущность Company связана с City, который принадлежит Province, который принадлежит Country:

Совокупные корни

Теперь предположим, что у нас есть страница со списком компаний, на которой перечислены некоторые компании с указанием их города, провинции и страны.

Справка по ID

Если объекту нужно ссылаться на City, Province или Country, они будут делать это по идентификатору (как было предложено Воном Верноном).

Чтобы получить эти данные из репозиториев, нам нужно вызвать 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);

Но, насколько я понимаю, на эти сущности нельзя ссылаться, поскольку они существуют в разных агрегатах.

Вопрос

Как еще я мог бы лучше спроектировать эти агрегаты?

Могу ли я загрузить объекты компании с помощью модели города, даже если они существуют в разных агрегатах? Я полагаю, что это скоро сломает границы между агрегатами. Это также создало бы путаницу при работе с согласованностью транзакций при обновлении агрегатов.


comment
Репозитории предназначены для совокупного хранения, а не для сложных чтений. jefclaes.be/2014/01/repositories- where-we-go-Ошибка_26.html   -  person JefClaes    schedule 03.02.2014


Ответы (2)


Деннис Трауб уже указал, что вы можете сделать для повышения производительности запросов. Этот подход намного более эффективен для запросов, но также более громоздок, потому что теперь вам нужен дополнительный код, чтобы синхронизировать вашу модель представления с агрегатами.

Если вам не нравится этот подход или вы не можете использовать его по другим причинам, я не думаю, что первый подход, который вы предлагаете, более неэффективен или громоздок, чем использование прямых ссылок на объекты. Предположим на мгновение, что вы использовали прямые ссылки на объекты в агрегатах. Как бы вы сохранили эти агрегаты в надежном хранилище? При использовании базы данных на ум приходят следующие варианты:

  • Если вы используете денормализованную таблицу для Company (например, с базой данных документов, такой как MongoDB), вы уже эффективно оптимизируете запрос представления. Однако вам потребуется вся дополнительная работа, чтобы ваша Company таблица синхронизировалась с City, Province. Эффективно, но громоздко, и вместо этого вы можете рассмотреть возможность сохранения реальных моделей представления (по одной для каждого варианта использования).
  • Если вы используете нормализованные таблицы с реляционной базой данных, вы должны использовать внешние ключи в таблице Company для ссылки на соответствующие City, Province и т. Д. По их идентификатору. При запросе Company, чтобы получить поля City, Province и т. Д., Которые необходимы для заполнения вашей модели представления, вы можете либо использовать JOIN для более чем 4 таблиц, либо использовать 4 независимых запроса к City, Province, .. .таблицы (например, при использовании отложенной загрузки для ссылок внешнего ключа).
  • Если вы используете нормализованные таблицы в нереляционной базе данных, обычно люди используют сторона приложения присоединяется точно так же, как в предложенном вами коде. Для некоторых баз данных инструменты ORM, такие как Morphia или Datanucleus, могут сэкономить вам некоторую работу по программированию, но под капотом независимые запросы остаются.

Следовательно, во втором и третьем вариантах вы сэкономите немного тривиальной работы по программированию, если позволите решению ORM сгенерировать отображение базы данных для вас, но вы не получите значительного повышения эффективности. (JOINs можно оптимизировать с помощью правильных индексов, но сделать это правильно - нетривиально).

Однако я хотел бы отметить, что вы сохраняете полный контроль над построением объекта модели представления и запросами к базе данных, когда вы ссылаетесь по идентификатору и используете программные соединения на стороне приложения, как в предложенном вами коде. В частности, названия городов, провинций и т. Д. Обычно меняются очень редко, их немного, и они легко вписываются в память. Следовательно, вы можете широко использовать кэширование в памяти для запросов к базе данных - или даже использовать репозитории в памяти, которые заполняются из плоских файлов при запуске приложения. Если все сделано правильно, для построения вашей модели представления для Company требуется только один вызов базы данных для Company таблицы, а другие поля извлекаются из кэша / репозитория в памяти, что я считаю чрезвычайно эффективным.

person Alexander Langer    schedule 02.02.2014

Вы можете создать совершенно другой объект (который будет просто плоской структурой данных), который представляет модель представления и может быть напрямую получен из базы данных. Google "Тонкий слой чтения" или "CQRS".

person Dennis Traub    schedule 31.01.2014