DDD每笔交易一笔交易-跟踪案例

时间:2019-05-16 16:18:24

标签: oop domain-driven-design

说我有两个聚合A和B。我在A上使用工厂方法创建B。我还要求A不能产生x个以上的B实例。

具有以下实现看起来很自然:

BmSecurity

但这会违反修改两个聚合的规则:创建B和修改A。

如果我尝试遵守此规则,我将: 1.在A中创建B并引发类似BCreated的事件。 2.通过处理BCreated事件来更新A在下一个事务中的总数。

对我来说,在这个特定示例中,这看起来像是一个怪异的解决方法,因为在对A调用createB()方法之后,我将其置于不一致的状态。

我想念什么吗?

3 个答案:

答案 0 :(得分:0)

为简单起见,我将这些汇总更改视为工作单元。唯一的建议是您必须处理比赛条件。 现在,如果您想以一个最终的解决方案为例,则必须建模一个对A和B更改的事务建模的集合:BCreation A.requestBCreation更改状态并发出事件(BCreationAllowed)BCreation做出反应,然后BCreation调度一个命令来创建B并处理其后果域事件BCreated或BCreationRejected。 BCreation聚合侦听这些事件中的任何事件,依此类推。这可能是一个复杂且设计过度的解决方案。您还必须处理竞争条件,以便“同步”流程集合。 如果使用actor模型,一切都会变得更加容易。

答案 1 :(得分:0)

  

我想念什么吗?

您不会错过任何简单,不。

您面临的问题的总称是set validation -如果您要在一组数据上保持不变,那么所有修改该数据的操作都必须通过相同的单个锁。 >

当所有世界都是单个关系数据库时,该锁可能是隐式的-数据库本身正在按某种(逻辑上)序列化的顺序处理所有事务,因此,在一定谨慎的情况下,您可以确定保持不变,因为,在存储级别上,每笔交易都是全部或全无。

但是,如果您在两个数据库中分配该数据,则所有选择都将关闭。

另一种思考方式:如果您的事务仅在所有不同的“聚合”都存储在同一数据库中时才可以工作,则表明您真正拥有的是更大的聚合,隐式和隐藏在实现中细节-扩展规模将更加昂贵。

通常,我们可以放宽不变量-尽最大努力维护不变量,但也要检测违规并定义补偿协议。

答案 2 :(得分:0)

尽管“每笔交易一个总计”是一个规则,但在某些情况下,它可能不会杀死您务实并忽略它。实际上,我认为在某些情况下,这根本不可行,甚至不可能通过其他任何方式获得。

话虽这么说,您绝对应该尽最大努力遵守该准则。您的情况并不罕见。例如,我会想到库存水平和机票(实际上也是库存水平)。

将操作分为两个不同步骤的唯一方法是跟踪进程。为此,您需要一个流程管理器,甚至可能需要一些消息传递,但这一切都很顺利。

要解决该问题,您首先需要使用一些相关标识符“保留”创建的内容。然后可以将其保存在事务A中:

    // begin tx (application layer)
    if (A.Reserve(id))
    {
        // we're good
        bus.Send(new RegisterBCommand
                 {
                    Id = id,
                    TheIdForA = theId
                    // other properties
                 }); // perhaps using Shuttle.Esb
    }
    // commit tx (application layer)

下一步将注册B实体,并可能发布BRegisteredEvent来继续该过程。

还有一点:如果A和B都生活在同一个有界上下文中,则通常只有A.CreateB()。实现稍微相似的另一种方法是使用集成有界上下文(例如您的业务流程BC),然后将CreateB()作为A的扩展方法,其中A和B在单独的BC中,但是业务流程层使用两个域。另一种方法是简单的工厂,或者只是在您的应用程序/域服务中对其进行更新。