我的编程空间,编程开发者的网络收藏夹
学习永远不晚

php中怎么实现事件溯源

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

php中怎么实现事件溯源

php中怎么实现事件溯源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

事件溯源(Event Sourcing)是领域驱动设计(Domain Driven Design)设计思想中的架构模式之一。领域驱动设计是面向业务的一种建模方式。它帮助开发者建立更贴近业务的模型。

在传统的应用程序中,我们将状态储存在数据库中,当状态发生改变时,我们即时更新数据库中相对应的状态值。事件溯源则采用一种截然不同的模式,它的核心是事件,所有的状态都来源于事件,我们通过播放事件来获取应用中的状态,所以它叫事件溯源。

在本文中,我们将运用事件溯源模式编写一个简化的购物车,以此分解事件溯源的几个重要组成概念。我们也将使用 Spatie 的事件溯源库来避免重复造轮。

在我们的案例中,用户可以添加,删除以及查看购物车内容,同时它具备两个业务逻辑:

购物车不可添加超过 3 种产品。当用户添加第 4 种产品时,系统将自动发出一个预警邮件。

要求以及声明

本文使用 Laravel 框架。本文使用特定版本 spatie/laravel-event-sourcing:4.9.0 以避免不同版本之间的语法问题。本文并非手把手的分步教程,你必须有一定 Laravel 基础才可以理解本文,请避免咬文嚼字,关注架构模式的组成结构。本文的重点是阐述事件溯源的核心思想,此库中对事件溯源的实现方式并非唯一方案。

领域事件(Domain Event)

事件溯源中的事件被称为领域事件,与传统的事务事件不同,它有以下几个特点:

它与业务息息相关,所以它的命名往往夹带业务名词,而不应该与数据库挂钩。比如购物车增添商品,对应的领域事件应该是 ProductAddedToCart, 而不是 CartUpdated。它是指发生过的事情,所以它一定是过去式,比如 ProductAddedToCart 而不是 ProductAddToCart。领域事件只可追加,不可以删除或者更改,如果需要删除,我们需要使用具备删除效果的领域事件,比如 ProductRemovedFromCart。

根据以上信息,我们构建三种领域事件:

ProductAddedToCart:

<?phpuse Spatie\EventSourcing\StoredEvents\ShouldBeStored;class ProductAddedToCart extends ShouldBeStored{    public int $productId;    public int $amount;    public function __construct(int $productId, int $amount)    {        $this->productId = $productId;        $this->amount = $amount;    }}


ProductRemovedFromCart:

<?phpuse Spatie\EventSourcing\StoredEvents\ShouldBeStored;class ProductRemovedFromCart extends ShouldBeStored{    public int $productId;    public function __construct(int $productId)    {        $this->productId = $productId;    }}


CartCapacityExceeded:

<?phpuse Spatie\EventSourcing\StoredEvents\ShouldBeStored;class CartCapacityExceeded extends ShouldBeStored{    public array $currentProducts;    public function __construct(array $currentProducts)    {        $this->currentProducts = $currentProducts;    }}

事件 ProductAddedToCart 和 ProductRemovedFromCart 分别代表商品加入购物车以及被从购物车中移除,事件 CartCapacityExceeded 代表购物车中商品超标,这是我们前面提到的业务逻辑之一。

聚合(Aggregate)

在领域驱动设计中,聚合(Aggregate)是指一组紧密相关的类,他们自成一体形成一个有边界的组织,边界外部的对象只可以通过聚合根(Aggregate Root)与此聚合交互,聚合根是聚合中的一种特殊的类。我们可以将聚合想象中一个家庭户口本,对此户口本进行任何操作,都必须通过户主(聚合根)。

聚合具有以下几个特点:

它确保核心业务的不变性。也就是说我们在聚合做验证,对违反业务逻辑的操作抛出异常。它是领域事件的产生地。领域事件在聚合根中产生。也就是说我们可在领域事件已完成业务要求。它自成一体,具有明显的边界,也就是说,只能通过聚合根调用聚合中的方法。

聚合是服务于业务逻辑的主要以及最直接的部分,我们使用它直观地为我们的业务建立模型。

综上所述,让我们构建一个 CartAggregateRoot 聚合根:

<?phpuse Spatie\EventSourcing\AggregateRoots\AggregateRoot;class CartAggregateRoot extends AggregateRoot{    public function addItem(int $productId, int $amount)    {    }    public function removeItem(int $productId)    {    }}

CartAggregateRoot 具备两个方法 addItem 和 removeItem,分别代表添加以及移除商品。

另外我们还需要加些属性来记录购物车内容:

<?phpuse Spatie\EventSourcing\AggregateRoots\AggregateRoot;class CartAggregateRoot extends AggregateRoot{    private array $products;    public function addItem(int $productId, int $amount)    {    }    public function removeItem(int $productId)    {    }}

private array $products; 将记录购物车中的商品,那么我们什么时候可以为其赋值呢?在事件溯源中,这是在事件发生以后,所以我们首先需要发布领域事件:

<?phpuse Spatie\EventSourcing\AggregateRoots\AggregateRoot;class CartAggregateRoot extends AggregateRoot{    private array $products;    public function addItem(int $productId, int $amount)    {        $this->recordThat(            new ProductAddedToCart($productId, $amount)        );    }    public function removeItem(int $productId)    {        $this->recordThat(            new ProductRemovedFromCart($productId)        );    }}

在调用 addItem 和 removeItem 事件时,我们分别发布 ProductAddedToCart 和 ProductRemovedFromCart 事件,与此同时,我们通过 apply 魔术方法为 $products 赋值:

<?phpuse Spatie\EventSourcing\AggregateRoots\AggregateRoot;class CartAggregateRoot extends AggregateRoot{    private array $products;    public function addItem(int $productId, int $amount)    {        $this->recordThat(            new ProductAddedToCart($productId, $amount)        );    }    public function removeItem(int $productId)    {        $this->recordThat(            new ProductRemovedFromCart($productId)        );    }    public function applyProductAddedToCart(ProductAddedToCart $event)    {        $this->products[] = $event->productId;    }    public function applyProductRemovedFromCart(ProductRemovedFromCart $event)    {        $this->products[] = array_filter($this->products, function ($productId) use ($event) {            return $productId !== $event->productId;        });    }}

apply* 是 Spatie 的事件溯源库自带的魔术方法,当我们使用 recordThat 发布事件时,apply* 会被自动调用,它确保状态的改动是在事件发布以后。

现在 CartAggregateRoot 已通过事件获取了需要的状态,现在我们可以加入第一条业务逻辑:购物车不可添加超过 3 种产品。

修改 CartAggregateRoot::addItem,当用户添加第 4 种产品时,发布相关领域事件 CartCapacityExceeded:

public function addItem(int $productId, int $amount){    if (count($this->products) >= 3) {        $this->recordThat(            new CartCapacityExceeded($this->products)        );        return;    }    $this->recordThat(        new ProductAddedToCart($productId, $amount)    );}

现在我们已经完成了聚合根工作,虽然代码很简单,但是根据模拟业务而建立的模型非常直观。

加入商品时,我们调用:

CartAggregateRoot::retrieve(Uuid::uuid4())->addItem(1, 100);

加入商品时,我们调用:

CartAggregateRoot::retrieve($uuid)->removeItem(1);

放映机(Projector)

UI 界面是应用中不可缺少的部分,比如向用户展示购物车中的内容,通过重播聚合根或许会有性能问题。此时我们可以使用放映机(Projector)。

放映机实时监控领域事件,我们通过它可以建立服务于 UI 的数据库表。放映机的特点是它可以重塑,当我们发现代码中的 bug 影响到 UI 数据时,我们可以重塑此放映机建立的表单。

让我们写一个服务于用户的放映机 CartProjector:

<?phpuse Spatie\EventSourcing\EventHandlers\Projectors\Projector;class CartProjector extends Projector{    public function onProductAddedToCart(ProductAddedToCart $event)    {        $projection = new ProjectionCart();        $projection->product_id = $event->productId;        $projection->saveOrFail();    }    public function onProductRemovedFromCart(ProductRemovedFromCart $event)    {        ProjectionCart::where('product_id', $event->productId)->delete();    }}

放映机 CartProjector

会根据监听的事件来增加或者删除表单 projection_carts,ProjectionCart 是一个普通的 Laravel 模型,我们仅使用它来操作数据库。

当我们的 UI 需要展示购物车中的内容时,我们从 projection_carts 读取数据,这和读写分离有异曲同工之妙。

反应机(Reactor)

反应机(Reactor)和放映机一样,实时监控领域事件。不同的是反应机不可以重塑,它的用途是用来执行带有副作用的操作,所以它不可以重塑。

我们使用它来实现我们的第二个业务逻辑:当用户添加第 4 个产品时,系统将自动发出一个预警邮件。

<?phpuse Spatie\EventSourcing\EventHandlers\Reactors\Reactor;class WarningReactor extends Reactor{    public function onCartCapacityExceeded(CartCapacityExceeded $event)    {        Mail::to('admin@corporation.com')->send(new CartWarning());    }}

关于php中怎么实现事件溯源问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

php中怎么实现事件溯源

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

php中怎么实现事件溯源

php中怎么实现事件溯源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。事件溯源(Event Sourcing)是领域驱动设计(Domain Driven Design)设计
2023-06-20

Laravel中的事件溯源实例代码分析

这篇文章主要介绍了Laravel中的事件溯源实例代码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel中的事件溯源实例代码分析文章都会有所收获,下面我们一起来看看吧。我们将新建一个 Laravel
2023-07-04

eBay如何通过事件溯源实现持续交付

这期内容当中小编将会给大家带来有关eBay如何通过事件溯源实现持续交付,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。eBay的持续交付团队通过使用以事件为中心的方法构建了一个可以持续交付的编配器,具备故障
2023-06-04

PHP中RCEService正则回溯怎么实现

今天小编给大家分享一下PHP中RCEService正则回溯怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。打开题目输入
2023-06-30

VB.NET中怎么实现编程事件

VB.NET中怎么实现编程事件,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。看一个关于VB.NET编程的例子,在这里呢我使用另一种方法来说明当你建立和注册一个事件处理程序时
2023-06-17

php中mongodb事务回滚怎么实现

在PHP中,MongoDB并不支持原生的事务处理。但是可以通过编写一些逻辑来模拟事务回滚的功能。以下是一个简单的示例:// 连接MongoDB$manager = new MongoDB\Driver\Manager("mongodb:
php中mongodb事务回滚怎么实现
2024-04-12

Android中怎么实现OnFocuChangeListener焦点事件

这篇文章将为大家详细讲解有关Android中怎么实现OnFocuChangeListener焦点事件,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。打开“res/layout/activity
2023-05-30

Android中怎么实现 View事件分发

这篇文章给大家介绍Android中怎么实现 View事件分发,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。(1)ViewGroup.dispatchTouchEvent(event)boolean dispatchTo
2023-05-30

怎么在JavaScript中实现事件委托

怎么在JavaScript中实现事件委托?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。事件委托利用事件冒泡,指定一个事件处理程序,管理一系列的所有事件事件委托利用DOM元素
2023-06-15

React中的合成事件怎么实现

这篇文章主要介绍了React中的合成事件怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇React中的合成事件怎么实现文章都会有所收获,下面我们一起来看看吧。1 事件三个阶段 捕获、目标、处理 (具体百度
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录