【问题标题】:How to access storage from domain layer如何从域层访问存储
【发布时间】:2011-05-19 06:57:48
【问题描述】:

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

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

  • 域代码不应访问存储(我认为...)
  • 获得独占存储锁意味着我将阻止对同样需要此访问的其他域对象的并发操作,因为只有在提交整个会话时才会释放锁。在这里使用乐观并发感觉很浪费:虽然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)有领域层访问服务层。在我看到的所有图表中,这是一个很大的禁忌。

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

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

如果它有所作为,我将使用 NHibernate 作为我的 ORM。

你会推荐什么方法?

【问题讨论】:

    标签: nhibernate domain-driven-design


    【解决方案1】:

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

    如果您对浪费 id 感到满意,那么生成新 id 的独立服务可能是您的最佳解决方案。然后,将此服务作为依赖项添加到其他获取 id 并将它们传递给域对象的服务。这样,您的域对象就不会受到任何外部交互的影响。

    【讨论】:

    • @Iulian 不幸的是,让 OrderService 生成 ID 不是一种选择,因为在我的实际场景中,Order 决定何时需要新 ID(它可能需要几个,或者根本不需要)。您对将 IdBroker 直接传递给域对象有何看法?
    • 在这种情况下,您可以将 IdBroker 传递给域对象。这通常称为DoubleDispatch。只需确保您将其作为具有最少所需方法的接口传递,以减少对其他组件的依赖(在您的情况下可能只是 GetBusinessId() 方法)
    • @Iulian 是的,这就是我打算做的。我遇到了另一个有趣的问题:服务必须打开一个单独的会话+事务,但我的工作单元不允许它(因为它已经在事务中)。这真的很烦人。关于解决这个问题的任何想法(除了使用线程池线程......)?
    • 您可以创建一个新的 TransactionScope 并传递一个 TransactionScopeOption.RequiresNew 作为参数。它应该为该范围创建一个新事务。
    • @Iulian 但我认为这将我绑定到 SQL Server,不是吗?我发现 Rhino commons 有一些看起来不错的东西。我会考虑使用它。感谢您的所有帮助!
    猜你喜欢
    • 2011-06-10
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2021-07-22
    • 1970-01-01
    • 2010-11-26
    • 1970-01-01
    • 2019-03-24
    相关资源
    最近更新 更多