一个根应该用于整个聚合图还是多个根应该在同一个图中?
Should one root be for the whole aggregate graph or should multiple roots be in the same graph?
我对设计中以下聚合的结构感到困惑。
一个根应该用于整个聚合图还是多个根应该在同一个图中?
我的情况:
工作时间规定
Id| Name | NumberOfAvailableRotations| IsActive
1| General Rule | 2 | true
工作时间
Id| Name | NumberOfHours| NumberOfShortDays |WorkTimeRegulationId
1 | Winter | 8 | 1 | 1
2 | Summer | 6 | 0 | 1
WorkTimeActivation
Id| StartDate | EndDate | IsDeFacto |WorkTimeId
1 | 2018-10-1 | 2018-12-30 | 1 | 1
Note: I consider (StartDate&EndDate) as DateTimeRange(valueobject)
现在我是否应该将 WorktimeRegulation
视为整个图形的根,以便它控制两者 (WorkTime,WorkTimeActivation
)?
WorktimeRegulation(ROOT)
|
V
WorkTime
|
V
WorkTimeActivation
或者我应该有两个像这样的聚合:
WorktimeRegulation(ROOT) | WorkTime(ROOT)
| | |
V | V
WorkTime | WorkTimeActivation
所以在第二个解决方案中我有两个根!但是,如果我将 WorkTime
视为根,它将可以与 WorktimeRegulation
分开使用,我不希望这样,因为这会破坏第一个聚合的完整性。
或者我应该有两个像这样的聚合:
WorktimeRegulation(ROOT) | WorkTimeActivation(ROOT)
| | |
V | V
WorkTime | WorkTime
基于评论:
Could you just explain the business problem this solves and what
invariants must be protected instead of the database structure
- 每个
WorkTimeRegulation
应该至少有一个WorkTime
才有效。因此,在创建 WorkTimeRegulation
时,我确保强制执行此不变量。
- 我需要检查已激活的
WorkTime
之间的任何冲突,因此之后我会根据最近的激活或用户 IsDeFacto
强制实施特定激活
WorkTime可以进行哪些行为?到目前为止我可以看到它可以
被激活。
是的,除了更新WorkTime
,还可以激活
为了验证激活你需要整个
激活历史记录还是仅最近的事件?
不是整个激活历史,我可以说是最近的单个激活历史。
激活规则是否跨越多个 WorkTime(例如,如果一个
active 那么另一个不能)?
Yes Per WorktimeRegulation
我的意思是如果我有 WorktimeRegulation
包含 3 WorkTimes
那么对于此规则只有一个有效的 Worktime
。
是否可以修改激活的名称、小时数等?
如果您的意思是 IsDeFacto
用于 Activation
那么是(通过此属性,如果同一 [=16= 的两次激活之间存在冲突,用户可以强制执行特定激活]), 因为你问题中前面的属性属于WorkTime
而不是Activation
,所以可以修改numberOfHours
.
修改这些细节是否会与其他业务冲突
操作(例如激活 WorkTime?
如果您的意思是these details
前面的属性,那么答案是否定的。
"如果相同的两次激活之间存在冲突
WorktimeRegulation” 你能详细说明什么样的冲突可以
发生?系统不应该阻止激活冲突吗?
发生?激活过程究竟是如何进行的?
根据业务专家的解释:这可能会发生冲突,系统不应该阻止它,但会向最终用户警告此冲突。
激活执行如下:
用户在WorkTimeRegulation
中选择一个特定的WorkTime
来激活,然后激活弹出窗口允许用户插入StartDate和预期的EndDate,当用户点击激活时,它应该检查是否与之前的激活有冲突在同一个 WorkTimeRegulation
中并提醒用户,如果他想要先前激活的优先级,那么他应该使用 IsDefacto
在发生冲突的情况下强制执行其中一个。
注意:终端用户事先并不知道激活工作时间的确切结束日期,所以他插入了预期的结束日期,因此可能会发生冲突。
让 WorktimeRegulation 指向一个
短时间内不存在的 WorkTime(例如
regulation.activateWorkTime(workTimeId) 然后激活工作
项目被删除)? WorkTime 可以得到 deleted/archived 吗?
WorktimeRegulation
不能指向不存在的WorkTime
工作时间至少激活一次不能删除或存档,刚刚激活和(在同一规定中激活其他工作时间时停用)
WorkTime 可以关联到不同的 Regulation 之后吗
创作?
没有
你确定这无关紧要,例如,姓名或
numberOfHours 在有人尝试更改的同时更改
激活工作时间?
现在我明白你的问题了,用户在第一次激活后无法更改工作时间。
我仍然不清楚为什么会发生激活冲突。
为什么要允许重叠的激活期,但随后
使用一个标志来指定要执行的标志。似乎很容易
而是防止激活期重叠,不是吗?真的吗
从业务角度来看,两个激活期可能
概念上重叠?为什么用户甚至输入 startDate 和
手动结束日期?你不能只跟踪日期
activation/deactivation发生在系统中?
根据业务专家的说法,特定 WorkTime 的激活开始日期是公司经理做出的决定(未计划)使用此 WorkTime
并在持续时间(无法预测,因为这是一个决定)公司经理决定切换到另一个 WorkTime,因此 HR 员工通过插入特定的开始日期和插入大致的结束日期来执行决定,因此下一次激活可能与最近的一个。
WorkTime 的实际业务含义是什么 activation/deact?
在这一年中,受特定WorkTimeRegulation
约束的员工,他们的WorkTimes
根据the activation
发生了变化,我的意思是WorkTime
可能从10月到12月是8小时,然后从1月到9月换成6小时(另一个WorkTime
),如此循环。
Note: The decision provides for start date only (e.g Winter WorkTime
Starts from date ...)
and not specifying the end date! So it is inserted roughly and as a
result the start date for the next activation may conflict with the
end date for the previous one)
是因为他们可能会提前创建激活吗?在那里面
情况下,IMO 是同一件事。强制执行结束日期
冲突激活恰好是主要激活的时间
starts 与拥有实际概念几乎相同,因为
当您使用 deFacto 强制激活另一个
重叠你隐含地声明另一个激活已经结束
当事实上的开始时,不是吗?
在询问了业务专家后,他是这样解释案例的:HR部门向公司经理发送了一个激活期(开始日期,预计结束日期)的建议,之后manager 确认了建议,HR 执行了建议,他们需要在当前 active worktime
结束日期之前提醒他们将下一个建议发送给公司经理,以便他们可以使用 defacto
强制执行下一个周期或只是激活不重叠 worktime
,所以是的,激活提前了时间
13.I 认为我们唯一需要解决的是是否多个
激活建议可以并行进行,规则是什么?
不能为同一个WorkTimeRegulation
并行提出多个激活建议,可以为多个WorkTimeRegulation
但不能为同一个WorkTimeRegulation
。
另外,建议审批流程是否需要建模
在系统中(例如跟踪谁批准了,也许通过上传
批准的电子邮件副本)?
没有这个过程是手动执行的,不需要建模。
最后,是否只能激活建议激活
那个被批准了?
是
第二个选项你错了。工作时间不属于作为子实体的第一个聚合。它只是作为根属于第二个聚合。第一个聚合将引用第二个聚合。你说你不想要它,因为它会破坏完整性。好吧,这完全是关于你想把交易边界放在哪里。如果您想在一次交易中更改三个实体,请寻求第一个意见(一个聚合)。如果您可以忍受延迟,请选择第二个选项(两个聚合),并在它们之间保持最终一致性。
更新:
看来你对聚合的理解是错误的。您的新绘图:
WorktimeRegulation(ROOT) | WorkTimeActivation(ROOT)
| | |
V | V
WorkTime | WorkTime
也错了。如果 WorkTime 是聚合中的子实体,则另一个聚合必须引用根,而不是子实体。除此之外,WorkTime 是一个实体。您不能将它绘制两次属于两个聚合。一个实体只属于一个聚合。
您有 3 个实体。你有这些聚合的可能性:
- 一个包含 3 个实体的集合。
- 两个聚合体:一个有 2 个实体;另一个实体。
- 三蕴:一蕴一实体。
一些基本规则:
- 如果聚合引用另一个聚合,它必须引用根,调用根提供的行为。
- 因此,一个聚合的子实体不能被另一个聚合的实体引用。
- 聚合的子实体可以引用另一个聚合根。
你必须研究的是,在事务中是否必须修改 3 个实体的状态以保持不变量相同,或者你可以将一个实体拆开,它本身就是另一个聚合。
即使这 3 个实体应该只形成一个聚合以保持不变量相同...您也可以拆分它,但是您必须将两个聚合与事件进行通信才能获得最终的一致性,因为您破坏了事务一致性。
Should one root be for the whole aggregate graph or should multiple roots be in the same graph?
视情况而定。
更重要的一点:它很少依赖于域实体之间的关系结构。
识别聚合边界主要是关于实体状态如何变化;特别是如果任何这些更改需要两个实体之间同时协调。
将实体组合成一个聚合使更改协调变得简单,但有一个成本 - 您不能再对实体进行独立的并发更改。独立的编辑要么需要互相阻塞等待,要么需要重新计算一个编辑。
将实体拆分为单独的聚合使并发编辑更加容易,但代价是实体之间的协调更改变得更加困难。
单个聚合确实希望拥有一个存储位置——试图协调两个不同位置的写入(也称为两阶段提交)是一种拖累。如果两个实体属于不同的聚合,该模型意味着它们可以存储在不同的设备中。
这也表明它们具有独立的生命周期——聚合的优势之一是清楚地了解一起归档的对象图。
要检查的另一件事是您的对象图是多个实体,还是具有描述其状态的复合值的单个记录。如果它可能是单个值,那么建议先尝试单个聚合。
您还可以查看数据来源,尤其是随时间变化的来源。如果更改总是来自相同的权威机构,那么它很可能是一个单一的聚合。另一方面,如果 WorktimeRegulation
和 Worktime
数据来自不同的来源,则更可能发生并发更改,这暗示存在多个聚合。
一个需要探索的重要问题:如果实体不完全一致,企业的成本是多少?如果存储在 Worktime 中的数据有一分钟不符合 WorktimeRegulation,有人会注意到吗?在您可以自由安排事情的情况下,您可以灵活调整更改顺序的情况下,倾向于多个聚合。
Two aggregates the WorkTime is the child in both of them
模型中的每个实体都应该属于一个集合;理论上,您可以在 WorktimeRegulation 中有一个 WorkTime,在 WorktimeActivation 中有一个 WorkTime,但是要让 same 工作时间在 WorktimeRegulation 聚合中 and 在 WorktimeActivation 聚合中是 "against the rules"。这意味着要么对 WorktimeActivation 的更改会影响 WorktimeRegulation,要么 WorkTime 本身应该分成两个独立的部分。
假设以下规则:
在任何给定时间,每个工作时间规定只能有一个有效工作时间。
在任何给定时间只能有一个待处理的工作时间激活建议。
不需要在系统中跟踪建议批准过程。
工作时间规定不能指向不存在的工作时间。工作时间一旦激活就不能删除或归档。
我认为对此进行建模是合理的,因此 WorkTimeRegulation
是一个包含 WorkTime
个实体集合的聚合根。此外,它必须对当前激活建议以及当前活动 WorkTime
进行建模,以执行上述规则。 suggestion/active 状态可以在 WorkTimeRegulation
本身或 WorkTime
实例上建模(在这种情况下,根必须确保一次只有一个处于活动状态,等等)。这确实是当时的设计偏好,两种策略都可以让您保护不变量。此外,WorkTime
实体还应跟踪它是否曾被激活,以防止对其其他属性进行进一步修改(例如 numberOfHours
)。
这是它的草稿:
上述模型将确保检查不变量所需的所有数据都是同一个根的一部分,并对存储状态和在内存中执行规则所需的最小结构建模。
但是,仅此模型并不能解决激活历史记录问题。您不需要整个历史来强制执行不变量,因此您不需要在 AR 的边界内对其进行建模,但业务肯定仍然需要此类数据。
此时您可以使用多种策略,例如:
- 使用事件溯源为您的 AR 建模,免费获取历史记录。
在存储当前 AR 状态的同时调度域事件并将它们存储在事件存储中,仍然允许您预测事件的历史。
为不可变的 ActivationHistoryEntry
AR 建模。例如。 historyEntry = regulation.activate(workTimeId); save(historyEntry); save(regulation);
。这与领域事件非常相似。
上述模型当然不完美,可以改进。例如,我还考虑过对 Activation
VO 的概念进行建模,而不是使用布尔标志,它可以是不同类型(活动的、非活动的、建议的)。这可能会允许一种更具表现力的语言,例如 ActiveActivation
只能通过激活 ActivationSuggestion
获得,其中 ActiveActivation
保留建议的结束日期(如果可用) , 以通知 HR 他们需要发送下一个建议)。
我对设计中以下聚合的结构感到困惑。
一个根应该用于整个聚合图还是多个根应该在同一个图中?
我的情况:
工作时间规定
Id| Name | NumberOfAvailableRotations| IsActive
1| General Rule | 2 | true
工作时间
Id| Name | NumberOfHours| NumberOfShortDays |WorkTimeRegulationId
1 | Winter | 8 | 1 | 1
2 | Summer | 6 | 0 | 1
WorkTimeActivation
Id| StartDate | EndDate | IsDeFacto |WorkTimeId
1 | 2018-10-1 | 2018-12-30 | 1 | 1
Note: I consider (StartDate&EndDate) as DateTimeRange(valueobject)
现在我是否应该将 WorktimeRegulation
视为整个图形的根,以便它控制两者 (WorkTime,WorkTimeActivation
)?
WorktimeRegulation(ROOT) | V WorkTime | V WorkTimeActivation
或者我应该有两个像这样的聚合:
WorktimeRegulation(ROOT) | WorkTime(ROOT) | | | V | V WorkTime | WorkTimeActivation
所以在第二个解决方案中我有两个根!但是,如果我将 WorkTime
视为根,它将可以与 WorktimeRegulation
分开使用,我不希望这样,因为这会破坏第一个聚合的完整性。
或者我应该有两个像这样的聚合:
WorktimeRegulation(ROOT) | WorkTimeActivation(ROOT) | | | V | V WorkTime | WorkTime
基于评论:
Could you just explain the business problem this solves and what invariants must be protected instead of the database structure
- 每个
WorkTimeRegulation
应该至少有一个WorkTime
才有效。因此,在创建WorkTimeRegulation
时,我确保强制执行此不变量。 - 我需要检查已激活的
WorkTime
之间的任何冲突,因此之后我会根据最近的激活或用户IsDeFacto
强制实施特定激活
WorkTime可以进行哪些行为?到目前为止我可以看到它可以 被激活。
是的,除了更新
WorkTime
,还可以激活为了验证激活你需要整个 激活历史记录还是仅最近的事件?
不是整个激活历史,我可以说是最近的单个激活历史。
激活规则是否跨越多个 WorkTime(例如,如果一个 active 那么另一个不能)?
Yes Per
WorktimeRegulation
我的意思是如果我有WorktimeRegulation
包含 3WorkTimes
那么对于此规则只有一个有效的Worktime
。是否可以修改激活的名称、小时数等?
如果您的意思是
IsDeFacto
用于Activation
那么是(通过此属性,如果同一 [=16= 的两次激活之间存在冲突,用户可以强制执行特定激活]), 因为你问题中前面的属性属于WorkTime
而不是Activation
,所以可以修改numberOfHours
.修改这些细节是否会与其他业务冲突 操作(例如激活 WorkTime?
如果您的意思是
these details
前面的属性,那么答案是否定的。"如果相同的两次激活之间存在冲突 WorktimeRegulation” 你能详细说明什么样的冲突可以 发生?系统不应该阻止激活冲突吗? 发生?激活过程究竟是如何进行的?
根据业务专家的解释:这可能会发生冲突,系统不应该阻止它,但会向最终用户警告此冲突。 激活执行如下: 用户在
WorkTimeRegulation
中选择一个特定的WorkTime
来激活,然后激活弹出窗口允许用户插入StartDate和预期的EndDate,当用户点击激活时,它应该检查是否与之前的激活有冲突在同一个WorkTimeRegulation
中并提醒用户,如果他想要先前激活的优先级,那么他应该使用IsDefacto
在发生冲突的情况下强制执行其中一个。 注意:终端用户事先并不知道激活工作时间的确切结束日期,所以他插入了预期的结束日期,因此可能会发生冲突。让 WorktimeRegulation 指向一个 短时间内不存在的 WorkTime(例如 regulation.activateWorkTime(workTimeId) 然后激活工作 项目被删除)? WorkTime 可以得到 deleted/archived 吗?
WorktimeRegulation
不能指向不存在的WorkTime
工作时间至少激活一次不能删除或存档,刚刚激活和(在同一规定中激活其他工作时间时停用)WorkTime 可以关联到不同的 Regulation 之后吗 创作?
没有
你确定这无关紧要,例如,姓名或 numberOfHours 在有人尝试更改的同时更改 激活工作时间?
现在我明白你的问题了,用户在第一次激活后无法更改工作时间。
我仍然不清楚为什么会发生激活冲突。 为什么要允许重叠的激活期,但随后 使用一个标志来指定要执行的标志。似乎很容易 而是防止激活期重叠,不是吗?真的吗 从业务角度来看,两个激活期可能 概念上重叠?为什么用户甚至输入 startDate 和 手动结束日期?你不能只跟踪日期 activation/deactivation发生在系统中?
根据业务专家的说法,特定 WorkTime 的激活开始日期是公司经理做出的决定(未计划)使用此
WorkTime
并在持续时间(无法预测,因为这是一个决定)公司经理决定切换到另一个 WorkTime,因此 HR 员工通过插入特定的开始日期和插入大致的结束日期来执行决定,因此下一次激活可能与最近的一个。WorkTime 的实际业务含义是什么 activation/deact?
在这一年中,受特定
WorkTimeRegulation
约束的员工,他们的WorkTimes
根据the activation
发生了变化,我的意思是WorkTime
可能从10月到12月是8小时,然后从1月到9月换成6小时(另一个WorkTime
),如此循环。
Note: The decision provides for start date only (e.g Winter
WorkTime
Starts from date ...) and not specifying the end date! So it is inserted roughly and as a result the start date for the next activation may conflict with the end date for the previous one)
是因为他们可能会提前创建激活吗?在那里面 情况下,IMO 是同一件事。强制执行结束日期 冲突激活恰好是主要激活的时间 starts 与拥有实际概念几乎相同,因为 当您使用 deFacto 强制激活另一个 重叠你隐含地声明另一个激活已经结束 当事实上的开始时,不是吗?
在询问了业务专家后,他是这样解释案例的:HR部门向公司经理发送了一个激活期(开始日期,预计结束日期)的建议,之后manager 确认了建议,HR 执行了建议,他们需要在当前 active
worktime
结束日期之前提醒他们将下一个建议发送给公司经理,以便他们可以使用defacto
强制执行下一个周期或只是激活不重叠worktime
,所以是的,激活提前了时间
13.I 认为我们唯一需要解决的是是否多个 激活建议可以并行进行,规则是什么?
不能为同一个WorkTimeRegulation
并行提出多个激活建议,可以为多个WorkTimeRegulation
但不能为同一个WorkTimeRegulation
。
另外,建议审批流程是否需要建模 在系统中(例如跟踪谁批准了,也许通过上传 批准的电子邮件副本)?
没有这个过程是手动执行的,不需要建模。
最后,是否只能激活建议激活 那个被批准了?
是
第二个选项你错了。工作时间不属于作为子实体的第一个聚合。它只是作为根属于第二个聚合。第一个聚合将引用第二个聚合。你说你不想要它,因为它会破坏完整性。好吧,这完全是关于你想把交易边界放在哪里。如果您想在一次交易中更改三个实体,请寻求第一个意见(一个聚合)。如果您可以忍受延迟,请选择第二个选项(两个聚合),并在它们之间保持最终一致性。
更新:
看来你对聚合的理解是错误的。您的新绘图:
WorktimeRegulation(ROOT) | WorkTimeActivation(ROOT)
| | |
V | V
WorkTime | WorkTime
也错了。如果 WorkTime 是聚合中的子实体,则另一个聚合必须引用根,而不是子实体。除此之外,WorkTime 是一个实体。您不能将它绘制两次属于两个聚合。一个实体只属于一个聚合。
您有 3 个实体。你有这些聚合的可能性:
- 一个包含 3 个实体的集合。
- 两个聚合体:一个有 2 个实体;另一个实体。
- 三蕴:一蕴一实体。
一些基本规则:
- 如果聚合引用另一个聚合,它必须引用根,调用根提供的行为。
- 因此,一个聚合的子实体不能被另一个聚合的实体引用。
- 聚合的子实体可以引用另一个聚合根。
你必须研究的是,在事务中是否必须修改 3 个实体的状态以保持不变量相同,或者你可以将一个实体拆开,它本身就是另一个聚合。
即使这 3 个实体应该只形成一个聚合以保持不变量相同...您也可以拆分它,但是您必须将两个聚合与事件进行通信才能获得最终的一致性,因为您破坏了事务一致性。
Should one root be for the whole aggregate graph or should multiple roots be in the same graph?
视情况而定。
更重要的一点:它很少依赖于域实体之间的关系结构。
识别聚合边界主要是关于实体状态如何变化;特别是如果任何这些更改需要两个实体之间同时协调。
将实体组合成一个聚合使更改协调变得简单,但有一个成本 - 您不能再对实体进行独立的并发更改。独立的编辑要么需要互相阻塞等待,要么需要重新计算一个编辑。
将实体拆分为单独的聚合使并发编辑更加容易,但代价是实体之间的协调更改变得更加困难。
单个聚合确实希望拥有一个存储位置——试图协调两个不同位置的写入(也称为两阶段提交)是一种拖累。如果两个实体属于不同的聚合,该模型意味着它们可以存储在不同的设备中。
这也表明它们具有独立的生命周期——聚合的优势之一是清楚地了解一起归档的对象图。
要检查的另一件事是您的对象图是多个实体,还是具有描述其状态的复合值的单个记录。如果它可能是单个值,那么建议先尝试单个聚合。
您还可以查看数据来源,尤其是随时间变化的来源。如果更改总是来自相同的权威机构,那么它很可能是一个单一的聚合。另一方面,如果 WorktimeRegulation
和 Worktime
数据来自不同的来源,则更可能发生并发更改,这暗示存在多个聚合。
一个需要探索的重要问题:如果实体不完全一致,企业的成本是多少?如果存储在 Worktime 中的数据有一分钟不符合 WorktimeRegulation,有人会注意到吗?在您可以自由安排事情的情况下,您可以灵活调整更改顺序的情况下,倾向于多个聚合。
Two aggregates the WorkTime is the child in both of them
模型中的每个实体都应该属于一个集合;理论上,您可以在 WorktimeRegulation 中有一个 WorkTime,在 WorktimeActivation 中有一个 WorkTime,但是要让 same 工作时间在 WorktimeRegulation 聚合中 and 在 WorktimeActivation 聚合中是 "against the rules"。这意味着要么对 WorktimeActivation 的更改会影响 WorktimeRegulation,要么 WorkTime 本身应该分成两个独立的部分。
假设以下规则:
在任何给定时间,每个工作时间规定只能有一个有效工作时间。
在任何给定时间只能有一个待处理的工作时间激活建议。
不需要在系统中跟踪建议批准过程。
工作时间规定不能指向不存在的工作时间。工作时间一旦激活就不能删除或归档。
我认为对此进行建模是合理的,因此 WorkTimeRegulation
是一个包含 WorkTime
个实体集合的聚合根。此外,它必须对当前激活建议以及当前活动 WorkTime
进行建模,以执行上述规则。 suggestion/active 状态可以在 WorkTimeRegulation
本身或 WorkTime
实例上建模(在这种情况下,根必须确保一次只有一个处于活动状态,等等)。这确实是当时的设计偏好,两种策略都可以让您保护不变量。此外,WorkTime
实体还应跟踪它是否曾被激活,以防止对其其他属性进行进一步修改(例如 numberOfHours
)。
这是它的草稿:
上述模型将确保检查不变量所需的所有数据都是同一个根的一部分,并对存储状态和在内存中执行规则所需的最小结构建模。
但是,仅此模型并不能解决激活历史记录问题。您不需要整个历史来强制执行不变量,因此您不需要在 AR 的边界内对其进行建模,但业务肯定仍然需要此类数据。
此时您可以使用多种策略,例如:
- 使用事件溯源为您的 AR 建模,免费获取历史记录。
在存储当前 AR 状态的同时调度域事件并将它们存储在事件存储中,仍然允许您预测事件的历史。
为不可变的
ActivationHistoryEntry
AR 建模。例如。historyEntry = regulation.activate(workTimeId); save(historyEntry); save(regulation);
。这与领域事件非常相似。
上述模型当然不完美,可以改进。例如,我还考虑过对 Activation
VO 的概念进行建模,而不是使用布尔标志,它可以是不同类型(活动的、非活动的、建议的)。这可能会允许一种更具表现力的语言,例如 ActiveActivation
只能通过激活 ActivationSuggestion
获得,其中 ActiveActivation
保留建议的结束日期(如果可用) , 以通知 HR 他们需要发送下一个建议)。