Reputation: 123
I am struggling to understand the right way to implement association mapping between two entities from different bounded contexts using Doctrine 2. Suppose that there are two "User" and "Post" entities that belong to "User" and "Content" bounded contexts, respectively. There is also a "User" concept in "Content" context that determines the author of a "Post" through a Many-To-One association. Therefore, "User" in "Content" context is simply a value object containing the user id.
My question is that how should I implement this association using Doctrine 2? I have two solutions that both have their own issues:
Solution 1:
/**
* @ORM\Entity
* @ORM\Table(name="posts")
* @ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* @ORM\ManyToOne(targetEntity="UserBC\User", inversedBy="posts")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
...
}
Solution 2:
/**
* @ORM\Entity
* @ORM\Table(name="posts")
* @ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* @ORM\Column(type="integer")
*/
private $user_id;
...
}
In the first solution, "User" entity from "User" context has been used inside "Content" context that violates DDD rules on BCs being independent of each other. The second solution respects DDD rules but affects database schema (removes database-level relationship between "users" and "posts" tables through a Foreign key constraint).
So, what is the right way to implement such associations?
Upvotes: 4
Views: 1370
Reputation: 19
Also for me the second solution is the correct one. And I try to answer to the @EresDev question:
"In solution 2, how do you know that $ user_id is correct and already exists?"
To do this you should use events. For example you could dispatch a PostPublicationEvent which contains the user id as well as post data. This event is listened by the User BC. Here you can verify that the user exists and dispatch a new UserValidatedEvent which is listened to by the Post BC. Now you can publish your post knowing that the user is valid.
Upvotes: 1
Reputation: 18024
The second solution is correct.
As you correctly observe, associations between different BCs should be avoided. The right way to reference an entity in another BC is by ID.
This has the consequence that the BCs don't have constraints between them in the DB. After all, you try to make them independent. If you feel that this is wrong, then the only way around this is to reconsider your BC design, i.e. merge the two BCs. This is however a decision that should not be driven by code smells, but by your context map.
Note: Referencing entities from other BCs by ID is only allowed if they are aggregate roots. If the referenced entity is not an AR, you have another design smell right there. Not a serious one, but still one that needs consideration.
Upvotes: 6