2016-11-11 110 views
1

我有一个租用实体,它是一个聚合根。除此之外,它还维护一个分配列表(保留的时间块)。何时以及为什么要使用域服务?

如何添加新的分配?由于Rental是聚合根,所以任何新的分配都应该经过它,但在我们尝试将分配保存在数据库中之前,无法说明是否可以分配租赁。另一位用户可能在此期间保留了它。我猜测,我应该为此使用域服务?

我不愿意注入任何东西每次我需要一个新的租赁但注入域服务,而不是一个存储库之间的区别,除了术语是不同的?

+0

您的系统非常庞大,您需要负载平衡并运行您的域逻辑的各种实例吗?如果不是这样,那么每个客户端都将通过您的有界上下文的相同实例进行租用,并且可以在您的域逻辑代码中解决冲突,而与数据库写入无关。 –

+0

是的。我们正在使用微服务架构,因此系统旨在同时运行预订服务的多个实例。 – Novac

回答

2

何时以及为什么要使用域服务?

您使用域服务来允许聚合运行查询。计税是一个不时显示的例子。汇总将某些状态传递给计算器,计算器报告税金,汇总决定如何处理该信息(忽略它,拒绝需要它的更新等)。

运行查询不会以任何方式修改域服务实例,因此您可以随心所欲地重复查询,而无需担心计算会互相影响。

认为只读服务提供商。

由于Rental是聚合根,所以任何新分配都应该经过它,但在我们尝试将分配保存到数据库之前,不可能说是否可以分配租赁。另一位用户可能在此期间保留了它。我猜测,我应该为此使用域服务?

否 - 完全错误的用例。

如果分配是租赁汇总的一部分,那么租赁汇总可以创建自己的分配也可以。您不需要为此提供服务(如果您喜欢分离问题,可以将工作委托给工厂)。

如果“另一个用户可能在此期间保留了该分配”,则表明存在争用 - 两个用户试图同时更改相同的聚合。这通常以两种方式之一进行管理。

锁定:您只允许一个用户每次修改租赁汇总。因此,在数据竞赛中,失败者必须等待胜利者完成,然后聚合可以拒绝失败者的命令,因为已经采用了该特定分配。

乐观并发:您允许两个用户同时修改聚合的不同副本,但只有原始状态不变时才允许保存。认为“比较和交换”;比赛是在保存,这两个指令

state.compareAndSwap(originalState, loserState) 
state.compareAndSwap(originalState, winnerState) 

之间

得主的比较和交换成功,但失败者的失败(因为originalState!= winnerState),所以失败者修改被拒绝。

无论哪种方式,只允许一次写入数据库保留分配。

如果我的理解正确,您是说在这种情况下可以使用租赁域实体内部的存储库吗?

不,您不应该需要 - 作为租赁聚合的一部分的分配由内存中的聚合创建,并且在保存聚合时首先出现在数据存储中。

为什么要使用聚合,如果所有结果都必须提取到周围的代码或工厂中?

这里的一些答案是关注点分离 - 聚合的主要关注点是强化业务不变性:确保创建具有某种特定状态的分配与其他所有事情保持一致。工厂负责确保创建的对象正确连接。

要使用您的示例:工厂将负责在内存中创建分配,但不需要知道确保分配是唯一的任何内容。确保分配是唯一的规则由汇总进行描述和执行。

+0

如果我的理解正确,你是说在这种情况下,可以使用租赁域实体内的存储库吗?我们正在运行这些服务的多个实例,因此在代码级别的任何锁定都不起作用。我可以研究乐观的并发性,但是我的领域模型感觉有障碍。我对域实体的操作变得无关紧要,因为任何状态更改或返回的值都是猜测,直到周围的代码检查数据库为止。如果所有的结果都被提取到周围的代码或工厂中,为什么要使用聚合? – Novac

+0

我仍然不完全确定我想如何处理我的具体问题,但现在我知道不该做什么。谢谢。 – Novac

0

使用静态工厂方法创建租赁对象。

public static class RentalFactory 
{ 
    public Rental CreateRental() 
    { 
     var allocationSvc = new RentalAllocationService(); 
     return new Rental(allocationSvc); 
    } 
} 

存储库只应该关注底层存储的持久性。 域服务首要关注的是执行一些涉及实体或值对象的行为。

+0

但是在这种情况下检查然后保存是不可靠的。为了让Rental集合返回任何可用的内容,它必须尝试将新的分配写入数据库 - 意思是试图持续这些信息。否则,Rental.Allocate方法将只是“添加到内部列表并且交叉手指”。 – Novac

相关问题