Мисля, че може да имам известно объркване относно разделянето на слоя домейн/услуга.
В моето приложение кодът на домейна трябва да генерира уникален идентификатор за цялата система с бизнес значение. Това означава, че трябва да има достъп до хранилището с изключителен достъп. Бих могъл да направя това директно в кода на домейна, но мисля, че това е грешно поради две причини:
- Кодът на домейна не трябва да има достъп до хранилището (мисля...)
- Получаването на изключително заключване на хранилището означава, че ще блокирам едновременни операции на други обекти на домейн, които също се нуждаят от този достъп, тъй като заключването ще бъде освободено само когато цялата сесия е ангажирана. Чувства се излишно да се използва оптимистична паралелност тук: докато генерирането на ID е бързо, бизнес операциите са сравнително дълги, което означава голяма вероятност от две едновременни бизнес операции.
Като пример разгледайте следното:
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) има недостатъка, че генерирането на ID не може да бъде върнато в случай на проблем в Order.Process, защото вече е ангажирано. Нямам проблем с това в моя сценарий. Добре ми е да губя документи за самоличност.
Ако има значение, използвам NHibernate като моя ORM.
Какъв подход бихте препоръчали?