DDD-Process discovery:质押式回购存间行情

About


我们尝试使用Domain Driven Design, Behavior-Driven Development, Event Storming, User Story Mapping等领域建模相关的方法,来完成这次的质押式回购存间行情。并将其中的一些过程记录在这里。由于业务性较强,也没能花大量篇幅介绍业务需求,所以此文可能有些难懂,但希望其中的过程和思考能对大家有所帮助,更希望能够收获到大家的宝贵意见。

Process discovery


首先,我们从需求中发现了以下一些定义:


Definitions.png

EventStorming


在完全理解需求和业务场景之后,我们对这个领域有了一个高层次的概述。下一步准备开始设计级别的EventStorming。我们在开始的领域探索中,使用事件、策略和规则建模所有可能的路径和场景。在这种情况下,可能很难发现特定的业务场景并对其进行优先级排序,因为它们将分布在整个墙中。因此,我们现在选择另一种方法:首先发现这些场景,并用设计级EventStorming分别对它们进行建模。

第一个步骤先进行场景映射:
我们使用示例映射来发现现实中可能发生的场景,在这过程中我们只按业务领域来分组示例,而不关注规则或技术方面的用例。

Finish a deal

Finish a deal.png
干涉 FROM 场务

干涉 FROM 场务.png
场务修改参数

场务修改参数.png
Command From 场务

Command From 场务.png
Start a new Day

Start a new Day.png

Design Level EventStorming


上述的示例映射可以成为我们设计事件风暴的基础,接下来我们就可以开始深入挖掘每个示例,识别与系统的关键交互,发现业务规则并不断改进模型。

Finish a deal

第一个场景是交易员通过X-Repo盘前达成撮合成交的场景:


prev-xrepo.png

你在这里可以看到,我们使用黄色便签来标记了交易员,把他作为了command的发起者,当交易员达成了X-Repo盘前撮合时,会下发对应的成交报告,此时形成了系统交互,因此这是我们放置一个蓝色的便签来表示一个名为“盘前撮合”的命令的原因。收到命令后行情系统需要对当然的开闭盘状态有一个了解(绿色-市场开盘状态的便签)。因为我们只在开盘前出来盘前撮合报价,所以我们可以确定一个规则(土黄色的便签),该规则描述了需要满足的条件才能触发之后的事件(棕色的便签)。同样的道理,我们在产生了“报告已收到”事件之后,需要通过“存款类机构列表”和“债券基础信息”来对报告进行对应规则的过滤,满足规则之后会触发“Report Collected”事件。之后的业务逻辑就不在这里详细说明了,我将在下面给出其他的EventStorming记录。


DEAL.png
干涉 FROM 场务

干涉 FROM 场务1.png

干涉 FROM 场务2.png
场务修改参数

场务修改参数1.png
场务修改参数2.png
场务修改参数3.png
场务修改参数4.png
场务修改参数5.png

Aggregates


在上面的事件风暴中可以看到,我们没有明确指定负责处理命令和发出事件的聚合。这种情况使我们很难将设计转换成具体的开发语言,因此我们现在需要通过上述的行为和责任去寻找到合适的Aggregate。在这里,你将看到我们如何计算出最终的聚合模型。

第一步我们把成交报告当作一个Aggregate。交易员的盘前X-Repo成交,盘中RFQ成交等等操作最终的将形成一份成交报告:


Report.png

接着,当我们看一看成交报告相关的一些规则:
成交报告中成交债券的债券类型为利率债
成交报告中买方机构与卖方机构都在存款类机构列表中
成交报告中质押券不为外币债或结算货币不为外币
成交报告中买方机构或卖方机构不为国开行
成交报告中回购方式不为三方回购和通用回购
成交报告中成交利率在利率区间内

从中,我们可以发现有很多机构、债券相关的规则,而成交报告的有效性以及一些潜在限制(实际上是一种属性/特征)似乎不如与机构、债券相关的限制那么重要。
那么,我们为什么不把机构、债券对象传递到成交报告的方法中呢,比如:

report.founudProduct(bond);
report.dealBy(institution);
report.dealFrom(institution);

(在成交报告关联买卖机构的时候还能通过Event进行头寸的扩展,这里就不拓展了。)
然后,如果机构、债券和成交报告的规则都通过了,我们将产生机构、债券和成交报告的聚合。聚合中的任何实体发生变化,都有可能触发对应事件,由对应的事件监听者处理后续逻辑。
在这需要额外提一下关于聚合中不同对象的规则间是存在一致性问题的,因为我们通过聚合中的一个动作影响了两个甚至更多个的实体。虽然在我们这个场景中没有体现,但在其他很多时候,这都是一个必须考虑的问题。

在成交报告生成之后我们需要开始逐笔行情,成交行情的计算。我们根据不同的事件,计算不同的行情,同时,行情又受收利率区间、开盘行情开关、盘前行情等因素影响,我们发现由不同Report产生的事件计算的可能是同一份行情,而不同行情之间又可能存在关联的关系。如果,我们再继续使用Report做为聚合根的话可能会使Report和行情实体的关系过于复杂。所以,这里我们分析了现实业务、行情的计算维度,决定以交易主体(交易品种)作为我们的聚合根,逐笔行情和成交行情都会围绕交易品种来计算。
在行情上下文中交易品种更关注的是它所关联的行情,而在其他上下文中交易品种会有他自己的业务规则,所以交易品种在不同上下文中表达的通用语言也应该是各不相同的。

Market.png

Bounded Context Classification


在寻找了Aggregate之后,我们开始分析模型中的限界上下文。到目前为止,我们已经确定了两个限界上下文——成交报告上下文和成交行情上下文。
成交报告和成交行情是有着密切上下关系的两个聚合,但我们期望他们分别能为更多不同的人服务,所以我将他们拆分成了两个不同的限界上下文。


Bounded Context.png

Project structure and architecture


先来看下这里的六边形架构模型,它可以使我们的领域和应用逻辑与框架(和基础设施)分开。这种方法的好处是,首先,我们可以在对最重要的部分——业务逻辑做单元测试时不需要任何依赖关系。其次,它提供了DIP(依赖倒置)原则(前面的中有对DIP的分析),为自己创造了一个调整Infrastructure层的机会,而不用担心破坏Domain的核心功能。
其次,正如上面的建模过程,整个架构除了确定限界上下文以为,都是由EventStorming驱动的。所以我们决定使用命令查询职责分离(CQRS)模式。其中,事件处理使用了Disruptor。

architecture.png

Coding model


Example in github


参考资料

设计原图

你可能感兴趣的:(DDD-Process discovery:质押式回购存间行情)