domain-driven-design - DDD 中的显式状态建模
问题描述
我一直在寻找管理 Root Aggregate 状态/生命周期,并发现了一些关于 usingExplicit State Modeling
或Explicit Modeling
over的好处的内容State Pattern
,它更简洁,我喜欢如何让我的域的明确概念处理它们自己的行为。
我读到的其中一篇文章是受“领域驱动设计的模式、原则和实践 - Scott Millett 和 Nick Tune”一书第 16 章的影响(这里是完整示例的代码示例)。
问题是这个想法描述得非常简单,没有太多内容,当我开始实施它时出现了,考虑到我是 DDD 的新手,概念开始重叠,这里有一些我想问的问题我希望 DDD 中更有经验的工程师会有所帮助,或者至少有人比我更好地解释了文本。
- 按照文章的示例,我将如何检索所有门(打开和关闭)的列表,这个结果集将映射到什么域实体?
- 如果所有显式状态模型都是实体/聚合,那么根聚合是什么?
- Root Aggregate 和那些显式建模的实体之间没有引用是否正常?
- 如果聚合根(比方说通用门实体)返回显式状态实体,存储库如何在不暴露实体或聚合状态的情况下保存它?
- 还是所有这些显式实体都是它们自己聚合的根?
我不希望得到以上所有答案,我只是分享被困在的想法,所以你可以看到我的立场,因为我分享代码有太多歧义,但我希望片段来自文章和书可以提供帮助。
一个 git 存储库或一个示例项目解决了其他具有显式建模的 DDD 组件将如何真正有用(我检查了一百万个存储库,但 90% 没有运气)。
注意:我没有使用 CQRS
来自媒体文章的示例:
interface ClosableDoor
{
public function close();
}
// Explicit State. (third attempt)
class CloseDoorService()
{
// inject dependencies
public function execute($doorId)
{
$door = $this->doorRepository->findClosableOfId($doorId);
if (!$door) {
throw new ClosableDoorNotFound();
}
$door = $door->close();
$this->doorRepository->persist($door);
}
}
书中的例子:
// these entities collectively replace the OnlineTakeawayOrder entity (that used the state pattern)
public class InKitchenOnlineTakeawayOrder
{
public InKitchenOnlineTakeawayOrder(Guid id, Address address)
{
...
this.Id = id;
this.Address = address;
}
public Guid Id { get; private set; }
public Address Address { get; private set; }
// only contains methods it actually implements
// returns new state so that clients have to be aware of it
public InOvenOnlineTakeawayOrder Cook()
{
...
return new InOvenOnlineTakeawayOrder(this.Id, this.Address);
}
}
public class InOvenOnlineTakeawayOrder
{
public InOvenOnlineTakeawayOrder(Guid id, Address address)
{
...
this.Id = id;
this.Address = address;
}
public Guid Id { get; private set; }
public Address Address { get; private set; }
public CookedOnlineTakeawayOrder TakeOutOfOven()
{
...
return new CookedOnlineTakeawayOrder(this.Id, this.Address);
}
}
解决方案
注意:我没有使用 CQRS
我认为这是你面临的最大挑战。
如果您没有尝试将它们用于可能不受为特定用例设计的显式模型的查询,那么为正在实现的用例检索显式建模的实体不会引起如此头痛。
我使用支持“表拆分”的实体框架,可以在这种情况下提供帮助。使用它,许多实体可以映射到同一个表,但每个实体都可以处理表中字段的子集并具有专用的行为。
// 用于一般查询
class Door
{
public Guid Id { get; private set; }
public State State { get; private set; }
// other props that user may want included in query but are not
// relevant to opening or closing a door
public Color Color { get; private set; }
public Dimensions Dimensions { get; private set; }
public List<Fixing> Fixings { get; private set; }
}
class DoorRepository
{
List<Door> GetDoors()
{
return _context.Doors;
}
}
// 用于开门用例
class ClosedDoor
{
public Guid Id { get; private set; }
public State State { get; private set; }
public void Open()
{
State = State.Open;
}
}
class ClosedDoorRepository
{
List<ClosedDoor> GetClosedDoors()
{
return _context.ClosedDoors.Where(d => d.State == State.Closed);
}
}
// 用于关门用例
class OpenDoor
{
public Guid Id { get; private set; }
public State State { get; private set; }
public void Close()
{
State = State.Closed;
}
}
class OpenDoorRepository
{
List<OpenDoor> GetOpenDoors()
{
return _context.OpenDoors.Where(d => d.State == State.Open);
}
}
推荐阅读
- python - 我为合并排序编写了一个递归程序。如何通过绘制其运行时间与python中的输入图来分析它的时间复杂度
- ruby-on-rails - 如何从代码中运行 Minitest 中的单个测试?
- python-3.x - 无法使用 accessToken 而不是 idToken - msal python
- javascript - 拍摄 Windy(传单)截图
- python - 大熊猫中按日期分组的分类区域堆栈图
- javascript - Js-IPFS 错误:cid.toBaseEncodedString() 不是函数
- android - 在 android 中使用 TextRecognizer
- bash - zsh:如何将变量扩展为多个参数?
- r - 使用 R 查找以前的素数
- android - Android x86 平台上surfaceflinger 进程的内存增长