DDD - 使用 Doctrine 2 的有界上下文之间的关联映射
DDD - Association mapping between bounded contexts using Doctrine 2
我正在努力理解使用 Doctrine 2 在来自不同限界上下文的两个实体之间实现关联映射的正确方法。假设有两个 "User" 和 "Post" 实体属于 "User" 和 "Content" 限界上下文。在"Content"上下文中还有一个"User"概念,它通过多对一关联来确定"Post"的作者。因此,"Content" 上下文中的 "User" 只是一个包含用户 ID 的值对象。
我的问题是我应该如何使用 Doctrine 2 来实现这个关联?我有两个解决方案,它们都有自己的问题:
解决方案 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;
...
}
解决方案 2:
/**
* @ORM\Entity
* @ORM\Table(name="posts")
* @ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* @ORM\Column(type="integer")
*/
private $user_id;
...
}
在第一个解决方案中,来自 "User" 上下文的 "User" 实体已在 "Content" 上下文中使用,这违反了 BC 彼此独立的 DDD 规则。第二种解决方案遵循 DDD 规则,但会影响数据库模式(通过外键约束删除 "users" 和 "posts" 表之间的数据库级关系)。
那么,实现这种关联的正确方法是什么?
第二种解法正确
正如您正确观察到的那样,应该避免不同 BC 之间的关联。在另一个 BC 中引用实体的正确方法是通过 ID。
这导致数据库中的 BC 之间没有约束。毕竟,你试图让他们独立。 如果您觉得这是错误的,那么解决这个问题的唯一方法就是重新考虑您的 BC 设计,即将两个 BC 合并。然而,这是一个不应由代码气味驱动的决定,而应由您的上下文映射驱动。
注意:仅当它们是聚合根时才允许通过 ID 从其他 BC 引用实体。如果引用的实体不是 AR,你就会有另一种设计味道。不是很严重,但仍然需要考虑。
同样对我来说,第二种解决方案是正确的。我试着回答@EresDev 的问题:
"在解决方案2中,你怎么知道$user_id是正确的并且已经存在?"
为此,您应该使用事件。例如,您可以调度包含用户 ID 和 post 数据的 PostPublicationEvent。该事件由用户 BC 监听。在这里您可以验证用户是否存在并发送一个新的 UserValidatedEvent,该事件由 Post BC 监听。现在您可以在知道用户有效的情况下发布您的 post。
我正在努力理解使用 Doctrine 2 在来自不同限界上下文的两个实体之间实现关联映射的正确方法。假设有两个 "User" 和 "Post" 实体属于 "User" 和 "Content" 限界上下文。在"Content"上下文中还有一个"User"概念,它通过多对一关联来确定"Post"的作者。因此,"Content" 上下文中的 "User" 只是一个包含用户 ID 的值对象。
我的问题是我应该如何使用 Doctrine 2 来实现这个关联?我有两个解决方案,它们都有自己的问题:
解决方案 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;
...
}
解决方案 2:
/**
* @ORM\Entity
* @ORM\Table(name="posts")
* @ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* @ORM\Column(type="integer")
*/
private $user_id;
...
}
在第一个解决方案中,来自 "User" 上下文的 "User" 实体已在 "Content" 上下文中使用,这违反了 BC 彼此独立的 DDD 规则。第二种解决方案遵循 DDD 规则,但会影响数据库模式(通过外键约束删除 "users" 和 "posts" 表之间的数据库级关系)。
那么,实现这种关联的正确方法是什么?
第二种解法正确
正如您正确观察到的那样,应该避免不同 BC 之间的关联。在另一个 BC 中引用实体的正确方法是通过 ID。
这导致数据库中的 BC 之间没有约束。毕竟,你试图让他们独立。 如果您觉得这是错误的,那么解决这个问题的唯一方法就是重新考虑您的 BC 设计,即将两个 BC 合并。然而,这是一个不应由代码气味驱动的决定,而应由您的上下文映射驱动。
注意:仅当它们是聚合根时才允许通过 ID 从其他 BC 引用实体。如果引用的实体不是 AR,你就会有另一种设计味道。不是很严重,但仍然需要考虑。
同样对我来说,第二种解决方案是正确的。我试着回答@EresDev 的问题:
"在解决方案2中,你怎么知道$user_id是正确的并且已经存在?"
为此,您应该使用事件。例如,您可以调度包含用户 ID 和 post 数据的 PostPublicationEvent。该事件由用户 BC 监听。在这里您可以验证用户是否存在并发送一个新的 UserValidatedEvent,该事件由 Post BC 监听。现在您可以在知道用户有效的情况下发布您的 post。