Reputation: 27862
So I organized my application into bounded contexts (Eric Evans' "domain-driven design"). One of the bounded contexts is the "gameplay context." It, for instance, contains an interface Gamer
public interface Gamer {
void setFriends(Set<Gamer> friends);
Set<Gamer> getFriends();
....
}
There is also an implementation that is able to persist a Gamer
's state to a database.
@Entity
public class JpaGamer implements Gamer {
private String someData;
private String someSensitiveData;
public setFriends (Set<Gamer> friends) {
...
}
...
}
Far, far away, inside another bounded context called "accounts context," I have classes and interfaces that deal with the users of my application. For instance, there is an interface called Account
.
public interface Account{
boolean isSignedUp();
....
}
So a user / Account
can be signed up or not. For any Account
, there exists a corresponding Gamer
.
I have a business rule: Never persist sensitive data anyhow related to a non-signed-up Account
.
For example, this means that some non-signed-up Account
's JpaGamer
instance cannot write data to the someSensitiveData
field. You could informally say that this JpaGamer
is a "non-signed-up JpaGamer
".
I don't want to hardcode any accounts-related logic into anything gameplay-related (and the same the other way around).
How can I implement such business rules in Java without tainting either bounded context with concepts from the other bounded context?
To fulfill the business rule, I have the idea that whenever there is a "non-signed-up JpaGamer
", I wrap that JpaGamer
inside a SparsePersistingGamer
. The SparsePersistingJpaGamer
would simply not forward to the underlying JpaGamer
any method that could potentially tamper with someSensitiveData
.
But now I have a problem with the someGamer.getFriends()
method. For the SparsePersistingGamer
, it would lazily load all that gamer's friends from JPA, returning a set of plain JpaGamer
s that are not aware of the (and any other) business rule, therefore persisting someSensitiveData
for potentially "non-signed-up JpaGamer
s".
Which strategies do you apply to tackle similar and related situations?
Upvotes: 1
Views: 1139
Reputation: 37739
Never persist sensitive data anyhow related to a non-signed-up Account.
This rule suggests that a Gamer
entity needs to know about its account status. I'm not familiar with your domain and therefore I can't comment on the boundaries, but lets assume that there are indeed two distinct BCs: gamers and accounts. This indicates that synchronization of some sort is called for - information from the Accounts BC needs to get to the Gamers BC. The information can be passed in a variety of ways.
One way is to denormalize the account status in each Gamer entity with an event driven architecture (EDA). This is not polluting the Gamers BC because the account status is an appropriate piece of information that affects entity behavior. The EDA can be set up such that the Account BC publishes events about status changes and the Gamers BC subscribes to those changes and updates the denormalized data in the Gamers entity. The advantage here is that the two BCs will be autonomous. A factor to consider however is eventual consistency - changes in the Account BC won't be transactionally consistent with the Gamers BC. This is often acceptable since the delay could be made increasingly small.
Another way is to provide the account status data to all behaviors on the Gamers entity that make use of that information. This information would be retrieved by an application service implementing use cases related to the Gamer entity. The GamerApplicationSerivce
would call out to the Accounts BC. The advantage of this approach is simplicity - no need to setup event handling. The downside is decreased autonomy - the Accounts BC has to be operational for the Gamer
use cases requiring account status info.
Also, I would recommend that you avoid using different entity implementations and thus avoid entity interfaces. This adds needless complexity and makes it more difficult to reason about behavior. Instead, express all domain logic explicitly in the entity and provide required data by the ambient application service or through event-driven denormalization.
Upvotes: 2