Создает ли шаблон репозитория ORM?
Помимо перевода реляционных данных в объектную модель, ORM имеет другие роли:
- Lazy Loading
- Автоматическое обнаружение изменений
- Сделки
Однако при преобразовании Репозитория, переводе ORM DTO на Модели доменов, это происходит:
- Невозможно использовать преимущества Lazy Load, так как мне нужно заполнить всю свою Модель домена и Репозиторий не знает, какие данные требуется домену.
- ORM не может обнаружить изменения, так как Модель домена не из мира ORM.
- Невозможно выполнить сразу несколько транзакций из-за отсутствия Домен знаний о ORM
Вопрос 1: Я пропустил какой-то пробел, где у меня могут быть полные преимущества ленивой загрузки, транзакций и автоматического обнаружения изменений в сообщении domain-driven-design сценарий? Или эти преимущества больше подходят для другого подхода (например, Active Record), чем для DDD?
Вопрос 2: Почему orm так упоминается в книгах DDD? Только для реляционной модели домена и Lazy Loading, Transactions и Change Detection полностью отключены
Некоторые платформы имеют подход, основанный на кодах, что позволяет улучшить эти проблемы, однако эта функция не всегда присутствует во многих средах или просто не может использоваться (например, в старой базе данных), поэтому она не решение.
Ответы
Ответ 1
В течение некоторого времени я думал, что если удалять отслеживание изменений из ORM, разработчики будут видеть в них гораздо меньшее значение.
Ленивая загрузка никогда не должна происходить. Агрегат всегда загружается целиком. Если вы обнаружите, что вам требуется ленивая загрузка, возможно, вы запрашиваете свою модель домена. Это то, чего вы не должны делать. Используйте для этого простой слой запроса/прочитайте.
Транзакции действительно являются проблемой БД и не будут напрямую влиять на DDD. Агрегаты представляют собой границу последовательности, поэтому транзакция базы данных является естественной подгонкой, но она заканчивается.
Вы все равно можете использовать инструмент ORM с DDD, но вы, вероятно, получите меньше пробега. Я вообще не люблю ORM, и если у меня есть выбор в этом вопросе, я просто их не использую. Отображение действительно не так много работает, и если это сделано в пользовательском классе mapper, выполняется на скорости языка, а не на каком-то прокси-механизме.
Есть случаи, когда я видел, где объекты домена, например, сохраняются напрямую с помощью ORM. Однако, если мне нужно отметить что-либо, используя, скажем, атрибут или даже изменить свой дизайн, где я должен реализовать определенные методы как virtual
или даже структурировать определенные классы определенным образом, я больше не считаю свой домен ненасытным и это то, что я действительно хочу (PI).
Ответ 2
Однако, с помощью шаблона репозитория, переводящего ORM DTO в домен Модели, которые происходят:
- Невозможно использовать преимущества Lazy Load, так как мне нужно заполнить всю мою модель домена, а репозиторий не знает, какие данные Domain потребности.
- ORM не может обнаружить изменения, так как доменная модель не из мира ORM.
- Невозможно совершить много транзакций сразу, опять же, из-за отсутствия знаний домена об ORM
Нет необходимости реализовывать другой уровень для привязки объектов ORM к объектам домена, когда используются современные ОРМ. В .NET-мире либо Entity Framework, либо NHiberate могут отображать вашу модель расширенного домена в реляционную базу данных.
Подход Code-First может использоваться с обоими из них.
Создана первая модель домена, затем создается база данных с помощью сопоставлений (Entity Framework Fluent Api/Fluent NHibernate Mappings) или соглашения (EF Custom Code First Conventions/Свободное навигационное отображение NHibernate)
Есть некоторые связанные вопросы SO:
Ответ 3
Почему ORM так упоминается в книгах DDD? Просто для отношения к модель домена и ленивая загрузка, транзакции и обнаружение изменений полностью отброшен
Вы можете найти реализацию DDD для системы Cargo, описанной в синей книге в http://dddsample.sourceforge.net/. Он использует Hibernate как ORM.
В приложениях есть транзакции базы данных, например, см. se.citerus.dddsample.application.impl.BookingServiceImpl
. @Transactional
представляет собой аннотацию из Spring, которая заставляет метод обертываться в транзакции базы данных.
Обнаружение изменений не отбрасывается. Репозиторий в исходном шаблоне DDD не имеет метода обновления, поэтому обнаружение изменений (ORM) используется для обновления объектов домена. Например:
@Override
@Transactional
public void assignCargoToRoute(final Itinerary itinerary, final TrackingId trackingId) {
final Cargo cargo = cargoRepository.find(trackingId);
if (cargo == null) {
throw new IllegalArgumentException("Can't assign itinerary to non-existing cargo " + trackingId);
}
cargo.assignToRoute(itinerary);
cargoRepository.store(cargo);
logger.info("Assigned cargo " + trackingId + " to new route");
}
Фактически в образце репозиторий имеет метод обновления, потому что cargoRepository.store()
- это метод обновления:
public void store(Cargo cargo) {
getSession().saveOrUpdate(cargo);
// Delete-orphan does not seem to work correctly when the parent is a component
getSession().createSQLQuery("delete from Leg where cargo_id = null").executeUpdate();
}
Удивительно, но вы можете найти использование ленивой коллекции в официальном образце, например, в src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml:
<hibernate-mapping default-access="field">
<class name="se.citerus.dddsample.domain.model.cargo.Cargo" table="Cargo">
...
<component name="itinerary">
<list name="legs" lazy="true" cascade="all">
<key column="cargo_id" foreign-key="itinerary_fk"/>
<index column="leg_index"/>
<one-to-many class="se.citerus.dddsample.domain.model.cargo.Leg"/>
</list>
</component>
</class>
</hibernate-mapping>
Итак, ответ заключается в том, что вы все еще можете использовать все свои ORM.