Communicating between two Bounded Contexts in DDD
Asked Answered
G

6

40

I have few different Bounded Contexts in the domain. The validation of a CRUD operation is built in each Bounded Context.

For example, I can create an entity called GAME only if the person creating it is a Group Leader.

I have two Bounded Contexts (BC) in this example. One is the Game BC and the other is the User BC. To solve the problem, in the Game BC, I have to make a domain service call like IsGroupLeader() to the User BC before proceeding on creating the Game.

I don't think this type of communication is recommended by DDD. I can have a User entity also in the Game BC, but I don't want to because the same User entity is being used differently in a different context in a different BC.

My questions are:

  1. Should I use Domain events where the Game BC has to send an event to the User BC asking the status of the User? With this approach, I don't make a synchronous call like IsGroupLeader but an event called is_group_leader. Then the Game BC has to wait for the User BC to process the event and return the status. The Game BC will create the Game entity only after the User BC process the event.

  2. Is CQRS a solution to my problem?

Any idea appreciated.

Gethsemane answered 23/5, 2013 at 11:42 Comment(0)
R
38

When integrating BCs, you have a few options. The reason that calling out to an external BC is discouraged is because it requires for both BCs to be operational at the same time. However, this is often quite acceptable and is simpler than the alternative. An alternative is to have the Game BC subscribe to events from the User BC and keep local copies of the data it needs, which in this case is information about whether a user is a group leader. In this way, when the Game BC needs to determine whether a user is a group leader, it doesn't need to call out to the User BC, it just reads locally stored data. The challenge of this event-driven alternative is synchronizing the events. You have the make sure the Game BC receives all appropriate events from the User BC. Another challenge is dealing with eventual consistency, since the BCs may be slightly out of sync at any given point in time.

CQRS is somewhat orthogonal to this problem.

Resign answered 23/5, 2013 at 14:59 Comment(4)
As per your alternative approach, when the user logs into the system, from the User BC, an event needs to be published stating the user is a Group Leader. This event then goes to the subscribers including the Game BC. Since this value can be reused again it can be cached in a dictionary with a key which can be that user. But this may demand more memory because 1000 users login to the system, the Game BC's cache grows fast! When does the cache can be cleared? The User BC to send another notification saying the user has logged out. I think there are more challenges here.Gethsemane
You don't need to capture the log on event, you need to capture the event which signals changes to the group leader status of a user. This would typically happen when the user is initially registered and/or when there is an explicit operation to change the user to or away from being a group leader.Resign
@Resign "An alternative is to have the Game BC subscribe to events from the User BC and keep local copies of the data it need", so since I'm now persisting Users in Game BC, does that make User it an entity in that BC? coz I'll need to differentiate between them according to their ids?Fanlight
@Resign The two bounded contexts use different databases and they keep different representations of the same data. In that case, do you have to worry if one BC fails to publish domain events so the other BC will be forever out of sync? Do you have to write a script that copies and overwrites from database in one BC to the other?Gloriole
T
19

Here is how I would reason about it.

I would argue that the Game BC doesn't know about "Users", it might however know about "Players".

If the Game BC is dependant on an active/current player then it should be passed into the BC when creating the Game BC instance.

eg.

 Player currentPlayer = GetPlayerSomehow...();
 GameBC gameBC = new GameBC(currentPlayer);
 gameBC.DoStuff();

Now your two BC's are still separate, you can test them separately etc.

And to make it all work you simply do something like:

 User currentUser = GetCurrentUser();
 Player currentPlayer = new Player();
 currentPlayer.IsGroupLeader = currentUser.IsGroupLeader;
 GameBC gameBC = new GameBC(currentPlayer);
 gameBC.DoStuff();

This serves as an anticorruption layer between the UserBC and the GameBC, you can move and validate the state you want from the UserBC into the state you need for your GameBC.

And if your GameBC needs to access many users, you can still pass some sort of mapping service into the game BC that does this kind of transformation internally.

Topcoat answered 24/5, 2013 at 7:47 Comment(1)
Thanks for the feedback. Let me tell little more about the domain language I'm talking about. I don' expect a Player to create a game. The group leader works for a City Recreation Club. The Recreation Club has many games like soccer, racket ball etc... The feature that I'm talking is creating a new game like Basketball. From your answer, the anti-corruption layer where we can inject a currentPlayer/currentGame is the domain service. A coordinating app service has to inject the user to the Game domain service and thus the domain in the BC can use it.Gethsemane
E
4

I think you're almost there. Close to a good solution. I'm not so sure you have to split these two into two BC's. Your User Aggregateroot (?) and Game maybe belong in one BC and depend on each other. A User "has a" Membership "to one or many" Games (just guessing your entity relations). But I'm just brainstorming now. Try to follow :) Different approaches follows:

First GameBC has a Create() method that actually take a UserMembership as param. Create(UserMembership). You then through UserMembership entity know what kind of membership and User this. If accepted ,game is created. If not exception is thrown or Game gets a broken rule message, depends on what approach you want to communicate back to client. The coordination can be done in application layer without leakage of domain knowledge.

Second You do as one of the other answers. You raise a CreateGameEvent within Game.Create(UserId) method. That Event is caught by an EventHandler (registered by IoC in Application startup) that resides in Application layer and look up UserMembership through repository. The small leakage of domain knowledge is that business rule that knows who are allowed yo do what are verified in application layer. This can be solved by letting the CreateGameEventHandler take UserId and RuleRef (can be string "CAN_CREATE_GAME" or enum) and letting a UserPermission object verify the permission. If not. Exception is thrown and catched in application layer. Drawback can be that you do mot want permission reference strings be hardcoded in Create method.

Third ...continues where second approach ends. You know that GameBC may not be the right place to do user permission lookups if you follow SRP principle. But the action is triggered around that method somehow. An alternative an be a Create(GroupLeader user). OR you can have Game.Create(User user) then do a validation that User is GroupLeader type. Create(GroupLeader) tells you what you need to call this method.

Last Maybe an alternative that I like more now when writing this. When you want to create an entity I usually let that Create(Save) method be on the repository. The IGameRepository interface is located next to Game Entity in domain assembly project. But You can also create a GameFactory that are responsible for starting the lifecycle of Game Entity. Here is also a good place to put Create method... GameFactory.Create(GroupLeader) { return new Game.OwnerUserId = GroupLeader.Id; } Then you just save it IGameRepository.Save(Game)

Then you have an intuitive and self describing way of telling other developers that "You must have a GroupLeader instance to create a Game".

Finally I hope you realize that you know the domain and you will figure out what suits you best. Be pragmatic and don't go Eric Evan hardcore. There is so many devs out there that are stuck in a "religion" in how things are gonna be done. Size of project, money, time, and dependencies to other systems etc. also affect how well you can be strict in doing DDD.

Good luck.

Eadith answered 24/5, 2013 at 13:12 Comment(4)
Thanks for the answer. I think you are advocating a single BC where the Game and the User entities reside. I'm liking the first part of the second solution, but I'm concerned about SRP. I had a discussion of the same on the DDD forum. Here is the link where Ramin and I discussed it. The app service is going to a place where the communicating can happen.Gethsemane
Well.. Yes and No :) I read you and Ramin's discussion and that spot on how I have done i previous projects. Letting cross cutting concers like security, caching, logging etc be placed on the side of the GUI --> application --> Domain. And you abstract this as services depending on interfaces that are injected into you GameService (?). So your application layer is the coordinator. But User as a concept and Entity must be in your Game BC. Since you need to keep track of who creted the game... right?Eadith
It is a business validation or a simply validation for the group leader. He can only create the games. I'm forced to use the User entity in the Game BC because I want to avoid the cross BC communications.Gethsemane
@MagnusBackeus If there are various other functional domains in the business, colocating users & game under the same bounded context will not be possible & ideally not desirable. In such a scenario, how can this be handledPolished
G
2

To cope with the kind of problems you are facing, we use bounded roles a modeling pattern that emerged over the years and proved to work very well. Bounded contexts are defined after semantical units that often, in enterprise organizations, can be mapped to specific roles.

That should be obvious considering that different roles face different problems and thus speak slightly (or entirely) different languages.

Thus, we always model the roles that interact with our application as a junction point between applicative requirements (infrastructure, persistence, UI, localization etc...) and business rules (the domain) and we code them in different modules (aka assemblies or packages).

As for your second question, CQRS can be a way to code the kind of interactions between BCs that you are describing, but I don't like it in this particular context.

Glimmer answered 23/5, 2013 at 16:12 Comment(3)
The Bounded Roles may be pattern if the usage of a BC is restricted by users. In my case, the call is IsGroupLeader(). That is just an example. Another example I can tell is HasGroupLeaderPaidMembership(). In this context, the game entity can be created only if the user is group leader as well as he has paid his membership. The Membership may be handled in a third BC. The question boils down to communication between BCs.Gethsemane
Using bounded roles, a GroupLeader class would expose a CreateGame(GameId, out Game) command. Proper exceptions would signal unpaid membership. Such class don't belong to any BC, thus no communication is involved. It's a sort of facade, but in a DDD environment.Glimmer
Thanks Giacomo for the comment. You may be mentioning about domain service which is a layer that is available for the outside world. But in my opinion, both the entities belong to two different BCs. So the domain services have to talk to each other to solve it. That is an option but that generates dependencies I believe.Gethsemane
G
1

I think I may have to follow a different approach where I will make the User entity part of the Game BC (the same entity is part of User BC also). I will use the Repository to read the IsGroupLeader flag from the db in the Game BC. This way, the dependency on the User BC is removed and no communication is needed with the User BC.

What do you think?

Gethsemane answered 24/5, 2013 at 0:53 Comment(0)
L
0

I would suggest to pass the role of user and user information to Game bounded context service and also have a GroupLeader value object inside Game BC. this way you can always know who is group_leader.

Longshoreman answered 6/11, 2019 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.