所以我将我的应用程序组织成有限的上下文(Eric Evans的“域驱动设计”)。其中一个有限的背景是“游戏背景”。例如,它包含一个接口Gamer
public interface Gamer {
void setFriends(Set<Gamer> friends);
Set<Gamer> getFriends();
....
}
还有一个实现能够将Gamer
的状态持久保存到数据库中。
@Entity
public class JpaGamer implements Gamer {
private String someData;
private String someSensitiveData;
public setFriends (Set<Gamer> friends) {
...
}
...
}
远在很远的地方,在另一个称为“帐户上下文”的有限上下文中,我有类和接口来处理我的应用程序的用户。例如,有一个名为Account
的接口。
public interface Account{
boolean isSignedUp();
....
}
因此,用户/ Account
可以注册或不注册。对于任何Account
,都存在相应的Gamer
。
我有一个商家规则: 从不 持久敏感数据无论如何与非注册<相关/ strong> Account
。
例如,这意味着某些未注册Account
的{{1}}实例无法将数据写入JpaGamer
字段。您可以非正式地说这个someSensitiveData
是“非注册JpaGamer
”。
我不想将任何与帐户相关的逻辑硬编码到与游戏相关的任何内容中(反之亦然)。
如何在Java中实现此类业务规则,而不会将有界上下文与其他有界上下文中的概念相混淆?
为了实现业务规则,我认为只要有“非注册JpaGamer
”,我就会将JpaGamer
包裹在JpaGamer
内。 SparsePersistingGamer
根本不会转发到可能会篡改SparsePersistingJpaGamer
的基础JpaGamer
任何方法。
但现在我遇到了someSensitiveData
方法的问题。对于someGamer.getFriends()
,它会懒散地从JPA加载所有玩家的朋友,返回一组不知道(和任何其他)业务规则的普通SparsePersistingGamer
,因此持久{{1} }对于潜在的“非注册JpaGamer
s”。
您采用哪种策略来解决类似和相关的情况?
答案 0 :(得分:2)
永远不要保留与非注册相关的敏感数据 帐户。
此规则表明Gamer
实体需要了解其帐户状态。我不熟悉您的域名,因此我不能评论边界,但我们假设确实有两个不同的BC:游戏玩家和帐户。这表明需要某种同步 - 来自BC账号的信息需要到达游戏玩家BC。信息可以以各种方式传递。
一种方法是使用事件驱动架构(EDA)对每个Gamer实体中的帐户状态进行非规范化。这不会污染游戏玩家BC,因为帐户状态是影响实体行为的适当信息。可以设置EDA,使得帐户BC发布关于状态改变的事件,并且游戏玩家BC订阅这些改变并更新游戏玩家实体中的非规范化数据。这里的优点是两个BC将是自治的。然而,要考虑的因素是eventual consistency - 帐户BC的变化与游戏玩家BC在交易上不一致。这通常是可以接受的,因为延迟可能会越来越小。
另一种方法是将帐户状态数据提供给使用该信息的Gamers实体上的所有行为。该信息将由实现与Gamer实体相关的用例的应用服务来检索。 GamerApplicationSerivce
将呼叫BC帐户。这种方法的优点是简单 - 无需设置事件处理。缺点是自主性降低 - 账户BC必须适用于需要账户状态信息的Gamer
用例。
另外,我建议您避免使用不同的实体实现,从而避免使用实体接口。这增加了不必要的复杂性,使得更难以推理行为。相反,在实体中明确表达所有域逻辑,并通过环境应用程序服务或通过事件驱动的非规范化提供所需的数据。