剖玄析微聚合 - 事件溯源
文章图片
本文转载自【何以解耦】:https://codedecoupled.com/php...
【剖玄析微聚合 - 事件溯源】聚合是 DDD(领域驱动设计)中一个相对复杂的概念。作为 DDD 战术设计中举足轻重的工具,我们有必要对其了若指掌。
总体来说,聚合(Aggregate)是指一组紧密相关的类,他们自成一体形成一个有边界的组织,边界外部的对象只可以通过聚合根(Aggregate Root)与此聚合交互:
文章图片
聚合常会与泛型 collection 混淆,他们的区别在于聚合是相对于领域模型而言,而 collection 是一种广泛的概念。那么具体来说,除了聚合根,聚合内部还有哪些类呢?本文阐述一个事件溯源的架构中,常见的几个重要组成部分(类)。
实体
实体为独立事物的建模工具,每个实体都拥有唯一标识符,比如 ID
, UUID
,Username
等等。大多数情况下,实体是可变的,它的状态会随着时间的迁移而改变。
实体很好理解, 如果我们用 DDD 来分析 ORM 业务,那么 ORM 中的 Model 可以理解为实体。数据库中的每一条数据都拥有自己的 ID(唯一标识符),每条数据的内容也会变化无常。
用实体为用户(User)建模:
class User extends Model
{
private int $id;
public function __construct(int $id)
{
$this->id = $id;
}public function changePassword(string $password)
{
// domain code
}}$user = new User(1);
$user->changePassword();
值对象 值对象是领域中用来描述,量化或者测量实体的模型。和实体不同,值对象没有唯一的标识符,两个对等的值对象是可以替换的。值对象具有不变性(Immutability),一旦创建以后,一个值对象的属性就定型了,不可更改。
值对象是一个强大但通常被忽略的建模工具。使用值对象建模可以大大简化我们的代码。下面我们来分析一下值对象几个特性。
无标识符性 值对象一定没有诸如
ID
, UUID
等等之类的标识符。不变性 值对象初始化以后,他的值变无法被更改。我们在用值对象建模时需要特别注意,不应该给值对象赋予改变其内部属性的功能。如果需要变异方法时,此方法必须创建一个全新的值对象(见以下
Money::add()
)。class Money
{
private int $amountInCent;
private string $currency;
public function __construct(int $amountInCent, string $currency)
{
$this->amountInCent = $amountInCent;
$this->currency = $currency;
}public function getAmountInCent(): int
{
return $this->amountInCent;
}public function getCurrency(): string
{
return $this->currency;
}public function add(Money $money): Money
{
if ($this->currency !== $this->getCurrency()) {
throw new \Exception('You can not add two different currency');
}return new Money (
$this->amountInCent + $money->getAmountInCent(),
$this->currency
);
}
}
可替换性 值对象的不变性奠定了其可替换性的基础,如果两个值对象的值对等,我们可以放心地替换两个值对象。我们可以这样理解这个概念:如果甲有一张10块钱人民币钞票,乙也有一张10块钱人民币钞票,他们可以进行对等交换而不会引起利益纠纷。
领域中很多模型都可以使用值对象来建模,它的特性给予我们一个安全的,灵活的环境来管理我们的代码。比如上节中的
$password
就可以使用值对象来完成。class Password
{
private string $value;
public function __construct(string $value)
{
$this->guard($value);
$this-> value = https://www.it610.com/article/$value;
}public function toString()
{
return $this->value;
}private function guard($value)
{
if (empty($value)) {
throw new /Exception('invalid password value');
}
}
}$user = new User(1);
$password = new Password($passwordStr);
$user->changePassword($password);
初始化成功后的
$password
一定是有效的,因为我们在初始化中做了防守。值对象为我们提供了一个安全的模型,接下来的代码中我们都无需担心 $password
是否符合业务要求。聚合根 聚合根的本质是实体,或者说聚合根是一种特殊的实体。聚合根的特殊性表现在它在聚合中的地位,它是聚合对外的唯一接口。如果我们把聚合想象成一个户口簿,那么聚合根就好像户主,任何户口簿的操作行为都需要通过户主。
领域事件 领域事件是领域中与业务息息相关的业务事件,它与基础设施,代码框架无关,所以领域事件不应该是诸如
EmailSent
,RecordUpdated
之类的极其广泛的框架事件。领域事件在聚合根中产生,然后被存储持久化。领域事件持久化是最后一个步骤,以此来确保所有的领域事件的正确性。如有任何异常,领域事件都不会被错误地保存起来。
总结 在一个事件溯源的架构中,一个聚合必定是由一个聚合根以及以上一个或多个部分组成。
本文转载自【何以解耦】:https://codedecoupled.com/php...,如果你也对 TDD,DDD以及简洁代码感兴趣,欢迎关注公众号【何以解耦】,一起探索软件开发之道。
推荐阅读
- 事件代理
- 事件处理程序
- Android事件传递源码分析
- 66万奔驰漏油事件看宝宝无理取闹
- 「我的2017」——2017|「我的2017」——2017,大事件盘点
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- 心理工作要不断的评估现实事件对来访者心理造成的影响是什么
- 如何做2020年年度复盘,写出100件成就事件
- 7、前端--jQuery简介、基本选择器、基本筛选器、属性选择器、表单选择器、筛选器方法、节点操作、绑定事件
- 事件解绑与解绑的兼容代码