Как определить идентификатор совокупного корня, добавленного в хранилище? - PullRequest
3 голосов
/ 08 июня 2011

Скажем, у меня есть общий интерфейс хранилища следующим образом:

public interface IRepository<T>
{
    Add(T item);
    Delete(int itemId);
    Update(T item);
}

Обычно новый идентификатор элемента, добавляемого через IRepository.Add (), будет определяться некоторой внутренней базой данных, но только один разОбщая транзакция / единица работы была представлена.Так что я совершенно уверен, что IRepository.Add () будет неправильно возвращать новый идентификатор добавленного элемента.Репозиторий действительно не должен ничего знать о том, как создаются идентификаторы.Это правильно?

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

Например, скажем, яесть сайт, где клиенты могут делать заказы.Новый клиент выбирает оформить заказ и отправляется на форму, чтобы заполнить свои данные.Эта информация используется для создания объекта Customer, который хранится в CustomerRepository.Теперь необходимо создать их информацию о заказе, но Заказ должен ссылаться на Клиента по его идентификатору?

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);
customerRepository.Add(newCustomer);

//How would I determine customerId??
Order newOrder = new Order(customerId, shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

Ответы [ 6 ]

3 голосов
/ 08 июня 2011

В приведенном вами примере я бы создал Customer и Order за один шаг и передал бы доменные объекты в доменные объекты вместо передачи идентификаторов:

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);

// Pass the customer rather than the CustomerId:
Order newOrder = new Order(newCustomer , shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

customerRepository.Add(newCustomer);
orderRepository.Add(newOrder);

// SaveChanges()

... когдаизменения сохраняются, структура автоматически заполняет идентификаторы Customer и Order и заполняет Customer.Id, Order.customerId и т. д. в силу того, что объект Customer был присвоен Order.

2 голосов
/ 08 июня 2011

Я генерирую идентификатор на клиенте (а-ля CombGuid.NewGuid()), а затем передаю его конструктору.Подход при использовании идентификатора базы данных имеет серьезные недостатки

1 голос
/ 08 июня 2011

Эрик

В сценарии, о котором вы упомянули, я не вижу никаких коммитов CommitChanges (). Я бы обернул все в область транзакций, а затем ударил бы customerRepository.CommitChanges (), прежде чем добавить строки заказа. После этого вы сможете получить идентификатор из вновь созданного объекта клиента и использовать его следующим образом:

Order newOrder = new Order(newCustomer.Id, shippingAddress, billingAddress);

тогда, если заказ (ы) не пройден, вы можете откатить все назад и сохранить его в атомарном состоянии, не нажимая на scope.Complete ().

надеюсь, это поможет ..

0 голосов
/ 06 января 2012

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

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

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

0 голосов
/ 09 июня 2011

Ваш интерфейс в большей степени DAO, чем репозиторий в соответствии с DDD:

http://codebetter.com/iancooper/2011/04/12/repository-saveupdate-is-a-smell/

Как упомянул Стив Уилкс, вы должны хранить ссылку на клиента в заказе, а не на идентификатор клиента, когдаРабота обработана, она создаст правильную связь этих сущностей в постоянном хранилище (БД SQL, веб-служба и т. Д.)

Подробнее о DAO здесь: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

0 голосов
/ 08 июня 2011

Независимо от того, используете вы NHibernate или нет, я чувствую, что подход, который он использует, является правильным.Ваша цель с любыми объектами домена состоит в том, чтобы в любой момент времени иметь в памяти только один экземпляр этого объекта, т.е. у вас никогда не должно быть двух объектов, представляющих одну и ту же запись базы данных.Из этого следует, что если база данных обновила запись с новым идентификатором, объект домена в памяти также должен быть обновлен с этим идентификатором, поскольку это «единое» истинное представление этой записи.

После вызова Add, идентификатор объекта установлен, и вы можете затем внести дополнительные изменения в этот объект и вызвать Update, не зная слишком много о вашей реализации.

...