You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Event Sourcing实践:如何实现Projection查询聚合列表?

如何在Python eventsourcing库中实现Projection以查询所有聚合条目?

问题描述

我正在学习Event Sourcing(事件溯源)设计模式,使用python eventsourcing库进行开发。已完成《Dog School》教程,但仍不清楚如何实现Projection(投影)。我认为若要查询聚合中的所有条目,需借助投影功能?似乎该库未内置投影,是否需自行编写查询语句从持久化层获取数据?

教程中的DogSchool类代码如下:

class DogSchool(Application):
    def register_dog(self, name):
        dog = Dog(name)
        self.save(dog)
        return dog.id

    def add_trick(self, dog_id, trick):
        dog = self.repository.get(dog_id)
        dog.add_trick(trick=trick)
        self.save(dog)

    def get_dog(self, dog_id):
        dog = self.repository.get(dog_id)
        return {'name': dog.name, 'tricks': tuple(dog.tricks)}

我希望实现如下的list_dogs方法,用于获取所有狗的列表:

def list_dogs(self):
        dogs = get_all_from_aggregate() #??
        return dogs

解决方案

eventsourcing库确实没有提供直接查询所有聚合的开箱即用API,但可以通过两种方式实现需求:

方式一:直接从仓库加载所有聚合(适合小型场景)

利用库的repository.get_all_ids()方法获取指定聚合类的所有ID,再逐个加载聚合并转换为所需格式:

class DogSchool(Application):
    # 保留原有的register_dog、add_trick、get_dog方法

    def list_dogs(self):
        # 获取所有Dog类型聚合的ID
        dog_ids = self.repository.get_all_ids(aggregate_class=Dog)
        dogs = []
        for dog_id in dog_ids:
            dog = self.repository.get(dog_id)
            dogs.append({
                'id': dog_id,
                'name': dog.name,
                'tricks': tuple(dog.tricks)
            })
        return dogs

这种方式实现简单,无需额外代码;但如果聚合数量多,逐个加载会影响性能——因为每个聚合都需要从事件流重建状态。

方式二:实现Projection投影(适合查询频繁/大数据场景)

投影的核心是维护一个只读视图,实时同步聚合的事件变化,查询时直接从视图获取数据,避免重复重建聚合。

步骤1:确保Dog聚合定义了对应事件

确认你的Dog聚合类已定义事件(教程中可能已包含,这里补充完整):

from eventsourcing.domain import AggregateRoot, Event

class DogRegistered(Event):
    name: str

class TrickAdded(Event):
    trick: str

class Dog(AggregateRoot):
    def __init__(self, name):
        super().__init__()
        self.name = name
        self.tricks = []
        self.__trigger_event__(DogRegistered(name=name))

    def add_trick(self, trick):
        self.tricks.append(trick)
        self.__trigger_event__(TrickAdded(trick=trick))
步骤2:创建投影类

定义投影类监听Dog的事件,维护存储视图:

from eventsourcing.domain import Projection

class DogProjection(Projection):
    def __init__(self):
        # 用字典存储视图,key为狗的ID,value为狗的信息
        self.dog_view = {}

    # 处理DogRegistered事件,初始化视图条目
    def apply_DogRegistered(self, event):
        self.dog_view[event.originator_id] = {
            'id': event.originator_id,
            'name': event.name,
            'tricks': []
        }

    # 处理TrickAdded事件,更新视图中的技巧列表
    def apply_TrickAdded(self, event):
        if event.originator_id in self.dog_view:
            self.dog_view[event.originator_id]['tricks'].append(event.trick)
步骤3:在Application中注册投影

修改DogSchool类,初始化并注册投影,让事件保存时自动触发投影更新:

class DogSchool(Application):
    def __init__(self):
        super().__init__()
        # 初始化投影
        self.dog_projection = DogProjection()
        # 订阅投影,监听Dog的所有事件
        self.event_store.subscribe(
            self.dog_projection,
            predicate=lambda e: isinstance(e, (DogRegistered, TrickAdded))
        )

    # 保留原有的register_dog、add_trick、get_dog方法

    def list_dogs(self):
        # 直接从投影视图获取所有狗的数据
        return list(self.dog_projection.dog_view.values())

这种方式查询性能高,视图实时更新;如果需要持久化视图(比如存到数据库),可以把字典替换为SQLAlchemy模型或其他持久化存储,在apply方法中操作数据库即可。


内容的提问来源于stack exchange,提问作者d s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 15:17:51