Я думаю, что у меня может быть некоторая путаница по поводу разделения домена / уровня обслуживания.
В моем приложении код домена должен генерировать общесистемный уникальный идентификатор с деловым смыслом. Это означает, что он должен получить доступ к хранилищу с эксклюзивным доступом. Я мог бы сделать это непосредственно в коде домена, но я думаю, что это неправильно по двум причинам:
- Код домена не должен иметь доступ к хранилищу (я думаю ...)
- Получение эксклюзивной блокировки хранилища означает, что я буду блокировать параллельные операции над другими объектами домена, которым также необходим этот доступ, поскольку блокировка будет снята только после фиксации всего сеанса. Использовать оптимистичный параллелизм здесь расточительно: хотя генерация идентификаторов быстрая, бизнес-операции относительно длинные, что означает высокую вероятность двух одновременных бизнес-операций.
В качестве примера рассмотрим следующее:
class UnitOfWork
{
public OrderRepository TheOrderRepository { get; private set; }
public void Commit() { /* ... */ }
}
class UnitOfWorkFactory
{
UnitOfWork CreateNewUnitOfWork()
{
var sess = CreateNewSession();
var trans = CreateNewTransaction(session);
return new UnitOfWork
{
TheOrderRepository = new OrderRepository(sess, trans);
}
}
}
class OrderService // application service layer
{
public void ProcessOrder(ID id, Details details)
{
using (uow = UnitOfWorkFactory.CreateNewUnitOfWork())
{
Order order = TheOrderRepository.Load(id);
order.Process(details);
uow.TheOrderRepository.Update(order);
uow.Commit();
}
}
}
class Order // domain layer
{
public void Process(Details details)
{
// need to get a unique, business-related identifier here.
// Where do I get it from?
}
}
Есть два варианта:
// Option 1 - get BusinessIdRepository from Service layer
class Order // domain layer
{
public void Process(Details details)
{
// bad, because other Orders will block in Process() until my Process()
// is done.
// also, I access the storage in my domain layer, which is a no-no (?)
var id = m_businessIdRepository.GetUniqueId(details);
}
}
И
// Option 2 - introduce a service. For this example, suppose the business ID is
// just an increasing counter
class BusinessIdBrokerService
{
int GetBusinessId(Details details)
{
int latestId;
using (uow = UnitOfWorkFactory.CreateNewUnitOfWork())
{
latestId = TheIdRepository.GetLatest(); // takes lock
latestId ++;
TheIdRepository.SetLatestId(latestId);
uow.Commit(); // lock is released
}
return latestId;
}
}
class Order // domain layer
{
public void Process(Details details)
{
// domain layer accessing the service layer. Is this bad?
var id = m_businessIdBroker.GetBusinessId(details);
}
}
Теперь, опция (1) имеет очевидные недостатки, но опция (2) имеет уровень домена, обращающийся к уровню обслуживания. На всех диаграммах, которые я видел, это большое нет-нет.
Следуя терминологии Джимми Богарда (http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/), похоже, я хочу здесь Службу домена (в отличие от Службы приложений), но эта Служба домена получит доступ к хранилищу (не только через хранилище: она создаст независимый сеанс) + транзакция.)
Следует отметить, что опция (2) имеет тот недостаток, что генерация идентификатора не может быть отменена в случае возникновения проблемы в Order.Process, поскольку она уже зафиксирована. У меня нет проблем с этим в моем сценарии. Я в порядке с тратить идентификаторы.
Если это имеет значение, я использую NHibernate в качестве ORM.
Какой подход вы бы порекомендовали?