Nhibernate 如何从域层访问存储

Nhibernate 如何从域层访问存储,nhibernate,domain-driven-design,Nhibernate,Domain Driven Design,我想我可能对域/服务层的分离有些困惑 在我的应用程序中,域代码需要生成具有业务意义的系统范围的唯一标识符。这意味着它必须以独占方式访问存储。我可以直接在域代码中这样做,但我认为这是错误的,原因有两个: 域代码不应该访问存储(我认为…) 获取独占存储锁意味着我将阻止对其他也需要此访问的域对象的并发操作,因为只有在提交整个会话时才会释放锁。在这里使用乐观并发感觉很浪费:虽然ID生成很快,但业务操作相对较长,这意味着两个并发业务操作的概率很高 作为一个例子,考虑如下: class UnitOfWo

我想我可能对域/服务层的分离有些困惑

在我的应用程序中,域代码需要生成具有业务意义的系统范围的唯一标识符。这意味着它必须以独占方式访问存储。我可以直接在域代码中这样做,但我认为这是错误的,原因有两个:

  • 域代码不应该访问存储(我认为…)
  • 获取独占存储锁意味着我将阻止对其他也需要此访问的域对象的并发操作,因为只有在提交整个会话时才会释放锁。在这里使用乐观并发感觉很浪费:虽然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);
    }
}
以及:

现在,选项(1)有明显的缺点,但选项(2)有访问服务层的域层。在我看到的所有图表中,这是一个很大的禁忌

遵循Jimmy Bogard的术语(http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/)看起来我想要一个域服务(而不是应用程序服务),但是这个域服务将访问存储(不仅通过存储库:它将创建一个独立的会话+事务)

我应该注意到选项(2)的缺点是,如果Order.Process出现问题,则无法回滚ID生成,因为它已经提交。在我的场景中,我对此没有问题。我不介意浪费身份证

如果有区别的话,我会用NHibernate作为我的ORM


您建议采用什么方法?

要删除订单对IdBroker的依赖关系,可以将IdBrokerService注入OrderService,并将新Id作为参数传递给Order.Process方法


如果您不介意浪费id,那么在您的情况下,生成新id的独立服务可能是最好的解决方案。然后将此服务作为依赖项添加到获取ID并将其传递给域对象的其他服务。这样,您的域对象将远离任何外部交互

@Iulian让OrderService生成ID不幸不是一个选项,因为在我的实际场景中,订单决定何时需要新ID(它可能需要几个,或者根本不需要)。您对将IdBroker直接传递给域对象有什么看法?在这种情况下,您可以将IdBroker传递给域对象。这通常被称为。只要确保将其作为一个接口传递,并使用所需的最少方法来减少对其他组件的依赖性(在您的示例中,可能只是一个GetBusinessId()方法)@Iulian是的,这就是我打算做的。我遇到了另一个有趣的问题:服务必须打开一个单独的会话+事务,但我的工作单元不允许它(因为它已经在事务中)。这真的很烦人。解决这个问题有什么想法(除了使用线程池线程…)吗?您可以创建一个新的TransactionScope并将TransactionScopeOption.RequiresNew作为参数传递。它应该为该范围创建一个新事务。@Iulian,但我认为这会将我绑定到SQL Server,不是吗?我发现Rhino commons有一些看起来不错的东西。我将研究如何使用它。谢谢你的帮助!
// 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);
    }
}