- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我面临一个设计问题,我想在两个不同的有界上下文中对同一物理对象进行建模。
为了尽可能准确地描述我的问题,甚至我意识到这只是一个实现细节,我将从事件源机制开始。
我的事件存储机制
以下内容受到Greg Young的CQRS文档https://cqrs.wordpress.com/documents/的广泛启发(请注意PDF“构建事件存储”部分)。
我有 2个表,一个表称为Aggregates
,另一个表称为Events
(注意,复数形式是表,而不是对象!)看起来像这样:
汇总表
我所有的汇总都存储在此表中;它具有3列(因此不支持md表格式,因此,抱歉,我会列出):
AggregateId
:基本上是该表的主键。我正在使用Guid,因为我的所有聚合都使用了Guid。AggregateType
:完全限定的聚合名称。 CurrentVersion
:当前的聚合版本。每次存储事件时都会增加的整数。 AggregateId
:聚集表的外键。 SerializedEvent
:聚集发布但以序列化形式(例如json)发布的域事件Version
:每次存储事件时(对于每个给定的聚合)递增的整数。 EventDate
:日期时间UserName
:发出事件的生产命令Product Entity
,其中包含的属性与Sales观点和Supply观点一样相关。
Product AR
namespace DomainModel/WriteSide;
abstract class AggregateRoot
{
protected $lastRecordedEvents = [];
protected function recordThat(DomainEvent $event)
{
$this->latestRecordedEvents[]=$event;
$this->apply($event);
}
protected function apply(DomainEvent $event)
{
$method = 'apply'.get_class($event);
$this->$method($event);
}
public function getUncommittedEvents()
{
return $this->lastestRecordedEvents;
}
public function markEventsAsCommitted()
{
$this->lastestRecordedEvents = [];
}
public static function reconstituteFrom(AggregateHistory $history)
{
foreach($history as $event) {
$this->apply($event);
}
return $this;
abstract public function getAggregateId();
}
namespace DomainModel/WriteSide/SupplyChain;
use DomainModel/WriteSide/AggregateRoot as BaseAggregate;
Class Product extends BaseAggregate
{
private $productId;
private $productName;
//some other attributes related to the supply chain BC...
public function getAggregateId()
{
return $this->productId;
}
private function __construct(ProductId $productId, $productName)
{
//private constructor allowing factory methods
}
public static function AddToCatalog(AddProductToCatalogCommand $command)
{
//some invariants protection stuff
$this->recordThat(new ProductWasAddedToCatalog($command->productId));
}
private function applyProductWasAddedToCatalog(DomainEvent $event)
{
$newProduct = new Product($event->productId);
return $newProduct;
}
//more methods there...
}
AddProductToCatalogCommand(/*...*/)
,Aggregates
表中插入新行:Events
表中插入新行:ProductWasAddedToCatalog($productId)
(当然是序列化形式)namespace DomainModel/WriteSide/Sales;
use DomainModel/WriteSide/AggregateRoot as BaseAggregate;
Class Product extends BaseAggregate
{
private $productId;
//some other attributes related to the Sales BC, like sales price, guarantees...
public static function AddAutomaticallyProductToCatalogSinceSupplyChainAddedIt(UpdateSalesCatalogCommand $command)
{
// some invariants' protection code here
$this->recordThat(new ProductWasAutomaticallyAddedToSalesCatalog($command->productId));
}
}
public static function reconstituteFrom(AggregateHistory $history)
时会收到另一个ID的事件
namespace DomainModel/WriteSide/Sales;
use DomainModel/WriteSide/AggregateRoot as BaseAggregate;
Class Product extends BaseAggregate
{
private $productId;
private $supplyChainProductId; //the reference to the supply chain BC Product AR...
public function getAggregateId()
{
return $this->productId;
}
//more methods there...
}
AggregateId
列作为主键,但是我可以同时使用
AggregateId
和
AggregateType
。
namespace DomainModel/WriteSide/Sales;
use DomainModel/WriteSide/AggregateRoot as BaseAggregate;
// **here i'd introduce my sub-entity**
use DomainModel/Sales/Product/Entities/Product as ProductEntity;
Class Product extends BaseAggregate
{
private $_Id;
private $product; //holds a ProductEntity instance
public function getAggregateId()
{
return $this->_Id;
}
public function getProductId()
{
return $this->product->getProductId();
}
//more methods there...
}
namespace DomainModel/QuerySide;
Class ProductMapping
{
private $productId;
private $salesAggregateId;
private $supplyChainAggregateId;
private $product; //holds a ProductEntity instance
public function getSalesAggregateId()
{
return $this->salesAggregateId;
}
public function getSupplyChainAggregateId()
{
return $this->supplyChainAggregateId();
}
}
Class ProductMappingRepository
{
public function findByproductId($productId)
{
//return the ProductMapping object
}
public function addFromEvent(DomainEvent $event)
{
//this repository is an event subscriber....
}
}
最佳答案
哇,这里有很多事。对AR建模既是一门艺术,又是一门科学!
第一点建议:设计AR时不要涉及数据库。为什么? CQRS,AR和事件源都是DDD的战略和战术模式。重点是消除建模过程中的干扰。数据库很分散注意力(在这种情况下)。这可能是您遇到困难的根本原因。
有限的上下文是简化建模的一种机制。它们应反映各个部门如何看待产品/物品等事物。实际上,该模型就是一个很好的例子。模型名称反映了企业在每种情况下使用的词。在某些方面,当他们谈论同一件事时,他们是不同的。它们在各自的上下文中具有不同的含义。因此,需要对它们分别建模。
那外部引用呢...
一个AR可以引用另一个AR,但只能以ID的形式(不一定是数据库密钥)。实际上,AR不得在其内部包含对另一个AR的引用。包含另一个AR的私有变量(具有a)。这是因为仅保证AR在其边界内保持一致。
这将我们带入问题中的问题。我们如何协调来自不同边界环境的这3个AR?
第一种方法是询问它们是否实际上处于不同的受限上下文中。有时,这些建模问题是引发对模型进行重新思考的有用方法。
让我们为您的域假设它们是正确的。我们如何协调他们?
在这种情况下,流程管理器和反腐败层似乎是一个不错的选择。流程经理将监听产品和/或项目创建的事件。然后它将生成用于创建其他实体的适当命令。每种情况有不同的机会。因此,需要ACL。 ACL负责将请求转换为在其域内有意义的内容。这可能很简单,只需将原始AR的外部ID添加到命令中以创建其AR。或者它可能只是将信息保存在暂存区域中,直到满足其他各种条件为止。
在较高级别上,听事件并在其他有界上下文中使用它们来触发相关过程。使用流程管理器(如果需要)和ACL。
最后是存储问题...
我会在这里选择一种简单的事件存储策略。将每个事件保持在流中。使用AR ID可以撤回任何单个AR的事件。
对于读取模型,我将使用一组侦听事件流的反规范器。然后,他们将生成针对UI定制的读取模型(在这种情况下)。这可能涉及合并来自不同BC的信息。对您的用户有意义的一切。
我在博客中的一篇文章4 Secrets to Inter Aggregate Communication中介绍了其中一些想法。
无论如何,我希望这会有所帮助。
关于domain-driven-design - DDD:建模聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29716944/
我正在寻找可以从 Eclipse 的 UML 图生成代码的工具。该工具应该从 UML 图生成代码,进行逆向工程以查看代码更改,并提供合并选项以防止因任何更改而删除文件。 是否有工具可以做到这一点,或者
我正在寻找有关建模的见解。我有一个关于设计模式和基本类图、序列图和用例的介绍类(class)。 我发现类图作为我编程中的组织工具非常宝贵。到目前为止,用例还算有用。 本学期我正在上课,更深入地学习 U
如何使用 flutter 数据建模技术访问“HH”和“mm”。我一直在尝试按以下方式使用它,但是下面给出了错误。 我的数据模型当前为它的简化版本。 class Week { final Strin
我正在使用 Neo4j 和 PHP。在我的项目中,我有餐厅节点。每个节点都有纬度、经度和分类属性。 我需要返回与用户给定分类相匹配的餐厅节点,其结果按距用户位置的距离排序(即第一个最近的餐厅)。 最简
我希望 POJO 包含一个 Status 字段(只有两个可能的值 SUCCESS、FAILED),并且根据此状态,第二个字段需要为 POJO 对象(如果 Status 为 SUCCESS) 或只是字符
我正在尝试将我的办公室迁移到数据库应用程序中。这是我得到的: (来源:phunkei.de) 公司和个人从客户继承(1:1关系)。这是因为他们都可以是“客户”并收到账单,我想引用订单中的单个表。公司和
我目前有这个数据库结构: 一个条目可以有多个"file"、“文本”和“url”类型的项目。 这些项目中的每一项在文本、网址或文件表(存储数据的位置)中都有一个对应的项目。 我需要一个查询来有效地选择一
下面的代码不应该像“if(condition)”语句一样工作吗,当它在另一个循环中并且“body”有自己的 break 或 continue 语句时,会出现明显的错误行为: for( ; condit
我一直在像这样存储和递增下载计数器: INCRBY downloads: 1 但现在我想要下载:* 按值排序,以便显示热门下载列表。 我觉得我可以更好地存储它。我愚弄了: ZINCRBY downlo
我开始使用图形数据库,在我的团队中,我们已经开始为我们的软件建模图形。当我们尝试“记录”模型以查看数据库的结构时,问题就出现了。对于 SQL 数据库,您只需查看 SQL 模式。 我们花了一些时间阅读
我正在做一个固定效应回归并且遇到了自相关问题,为了解决这个问题,我正在使用预测、lmtest 和 plm 包进行 ARIMA 建模。我的数据是一般面板数据,looks like this ,我正在尝试
我想知道是否有任何工具可以帮助我对 C 应用程序(即函数式编程)进行建模。例如。我目前正在构建一个共享库。但是为了直观地传达我的设计,我需要类似 UML 的东西。我想这样做,以便审查我的设计的人不需要
我正在尝试将 JSON 转换为 GSON ,但我无法建模。谁能给我举个例子。 [ { "id": "1", "name": "lalala",
正如标题所说,在 中建模 optional 参数的最佳方法是什么?斯卡拉 ? 对于 optional 参数,我的意思是执行函数体不需要的值。 要么因为该参数存在默认值,要么根本不需要该参数本身(例如配
我发现 UML 可用于记录 OO 系统的各个方面,尤其是用于整体架构的类图和用于说明特定例程的序列图。我想为我的 clojure 应用程序做同样的事情。我目前对模型驱动开发不感兴趣,只是在交流应用程序
我想知道是否有人知道如何使用 UML 在属性中建模 DateTime 基元类型? 最佳答案 我想这取决于日期的格式......请参阅下面的示例,其中我使用两个整数(分别为日和年)和一个名为“Month
我有一些与我的问题建模相关的问题。我正在研究基于模型的测试的论文项目。还想从专家的角度了解我是否采用正确的方法来建模我的场景。我正在对 Android 应用程序的 UI 进行建模,遍历它们,生成测试用
我正在尝试建立对 CouchDB 以及如何为某些现实世界场景建模数据的理解。我现在已经尽可能多地“按日期获取我的博客文章”;) 给定这样的文件: { "_id": "couch1",
一个模型资源是如何分层的?例如假设一个人有一个“留言板”并且“消息”是一种资源。假设“消息”可以有回复,从而形成一个讨论线程。如何模拟线程的概念? “消息”是否包含它的子项? “线程”是它自己的资源吗
我正在尝试创建一个像通用 POJO 一样工作的对象,因为我必须通过不同的对象传递它,并且我需要一个接口(interface)来访问其属性。 目前,我使用具有 getField 方法的基础对象来完成此操
我是一名优秀的程序员,十分优秀!