pyeventsourcing/eventsourcing: Python Event Sourcing with SQLite
A library for event sourcing in Python.
At a glance
- What is it?
- The eventsourcing package provides event-sourced aggregates, application services, persistence, snapshots, notifications, and optimistic concurrency control for Python 3.11 and newer. Its documented dog school example makes the first event-driven workflow easy to inspect, but adopting the library means accepting an event history as the source of application state.
- Who is it for?
- Adopt this project when the team wants a Python-native event history, CQRS projections, or application-level encryption and can design aggregate boundaries and event schemas for change. Do not choose it for a simple CRUD service where mutable tables already meet the requirements without replay, versioning, or projection complexity.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why eventsourcing changes a Python application's state model
eventsourcing is for applications where the history of changes matters, rather than only the latest row in a table. The README describes the design pattern directly: changes to application state are stored as a sequence of events. That sequence becomes the basis for reconstructing an aggregate, so the library is aimed at Python developers building systems that need auditability, event-driven processing, CQRS, or multiple projections from the same application events.
The package also fits teams working with domain models and consistency boundaries. Its domain layer includes an Aggregate class and an @event decorator, while its application layer supplies an Application class and repositories. The repository lists DDD, CQRS, distributed systems, Django, and SQLAlchemy as topics, but the README does not claim that every integration is part of this core package. Django and SQLAlchemy are referenced through extension projects, so those adapters should be evaluated separately from the core library.
This is not a general database wrapper or a replacement for ordinary SQL queries. It makes the event history part of the application design. That choice can help when a business domain needs to explain how state changed, but it also means that event definitions, aggregate versioning, replay behavior, and projection maintenance become engineering concerns. A service that only needs to store a user's current profile may find this model unnecessary.
From Aggregate events to application notifications
The documented architecture begins with domain events. In the Dog example, the constructor emits Registered and add_trick emits TrickAdded through @event. The aggregate mutates its own state when the event-producing method runs, then the application saves the aggregate and records the resulting events.
DogSchool wraps that model with application operations. register_dog creates a Dog and saves it, while add_trick loads an aggregate from the repository, changes it, and saves it again. get_dog converts the aggregate into a dictionary for querying. This separates domain behavior from application-level use cases while keeping the aggregate's identifier as the reference passed between operations.
The notification log provides a second path from the application state. The README's test selects notifications with start=1 and limit=10 and asserts that three notifications exist after registration and two trick additions. That mechanism is intended for pull-based propagation into replicas, indexes, view models, and other applications. It supports CQRS, but the repository documentation does not prescribe a particular projection framework, so the consumer side remains an application-specific design decision.
Persistence is selected through an event store assembled from an event mapper and event recorder. The recorder can be changed, and the README says that database recorders can be configured with environment variables. That separation is the key extension point for teams that need a persistence adapter beyond the default in-memory implementation.
Installing eventsourcing and running the documented DogSchool example
The README recommends installing the stable distribution from the Python Package Index inside a virtual environment. The project metadata specifies Python >=3.11.0 and lists eventsourcing as the package name. The basic installation command is:
$ pip install eventsourcingAfter installation, a first use can follow the README's DogSchool sequence. Define the aggregate and application in Python, then call the test function. The default persistence module stores events in memory using Python objects, so the example is suitable for inspecting the event-sourcing flow without first configuring a database.
The core model and application definition in the README is:
class Dog(Aggregate):
@event('Registered')
def __init__(self, name: str) -> None:
self.name = name
self.tricks: list[str] = []The application creates the aggregate, saves it, loads it from the repository, and saves a trick change. Calling test_dog_school registers Fido, adds two tricks, queries the resulting state, and selects notifications. The expected assertions are Fido, the two tricks, and three notifications.
For SQLite, the README documents these environment settings:
import os
os.environ["PERSISTENCE_MODULE"] = 'eventsourcing.sqlite'
os.environ["SQLITE_DBNAME"] = ':memory:'Set those variables before constructing the application, then call test_dog_school again. The documentation says that other persistence modules are available, but it does not give installation or configuration commands for each one.
Snapshots, versioning, and concurrency as explicit design choices
The library includes features that address problems created by long-lived event histories. Snapshotting reduces access time for aggregates with many domain events. This is a direct trade-off: snapshots add storage and reconstruction concerns, but can avoid replaying a long history every time an aggregate is loaded.
Event and aggregate versioning are documented as ways to introduce domain model changes after deployment. Older stored events and snapshots can be upcast to a newer representation before the object is reconstructed. This is useful for long-lived deployments, although the README does not document a rollback procedure or a complete policy for changing already persisted event shapes. Teams should therefore treat event schema evolution as a design task, not as an automatic guarantee.
Optimistic concurrency control is also included. The README describes it as protection against inconsistency caused by concurrent method execution in distributed or horizontally scaled applications, using optimistic concurrency controls in adapted database management systems. The precise conflict exceptions, retry behavior, and transaction boundaries are not described in the README, so those details need verification against the project documentation before relying on a particular retry strategy.
What the persistence and packaging boundaries leave unresolved
The default in-memory mode is useful for the documented test, but it is not a deployment description. SQLite is documented as an alternative, and the package metadata declares an optional postgres dependency. The README does not state which production database configuration is recommended for a particular workload, nor does it provide connection-pooling, backup, migration, or operational commands.
Application-level encryption and compression are supported according to the README. Encryption applies inside the application, which the documentation says means events are encrypted on the wire and at rest, including backups. Compression reduces stored event and snapshot size, with the README giving a usual range of around 25% to 50% reduction. Those are documented capabilities, not guarantees for every event type or deployment.
The project uses the BSD-3-Clause license. That permits use and modification under the license's conditions, but the LICENSE file should be reviewed before redistributing the package or embedding it in a product. The last push was on 2026-10-14, and the repository is not archived. This indicates recent repository activity but does not establish compatibility with a planned deployment. The repository has no GitHub releases, so package version information in project metadata is more relevant than a GitHub release history.
How this library differs from ordinary current-state persistence
The practical alternative is a conventional CRUD application that stores only current records in tables and updates those rows directly. Its read path is simple: query the current row. It does not need to reconstruct an aggregate from an event sequence or maintain a notification log for downstream views. That makes it a reasonable choice for many administrative tools, catalogs, and configuration services.
eventsourcing takes a different path. Commands run against aggregates, aggregate changes produce events, and state is obtained by replaying those events through the model. The application also exposes notifications for downstream consumers, while snapshots and upcasters address history size and schema evolution. The extra machinery is justified when the event sequence is part of the product requirement, such as auditable changes or projections. It is a poor fit when the team cannot define meaningful events or does not want to reason about historical representations.
The README's extension references are relevant to this comparison. Django and SQLAlchemy projects can adapt the library to popular persistence ecosystems, while the core package supplies the event store, domain model, application layer, and notification concepts. Those extension choices should be checked against the actual adapter's compatibility and maintenance status rather than inferred from the core repository.
Editorial conclusion
Adopt this project when the team wants a Python-native event history, CQRS projections, or application-level encryption and can design aggregate boundaries and event schemas for change. Do not choose it for a simple CRUD service where mutable tables already meet the requirements without replay, versioning, or projection complexity. Before adoption, verify Python 3.11 or newer availability, the required persistence module, the event evolution strategy, and whether the BSD-3-Clause licensing fits the intended distribution model.
Frequently asked questions
How do I install eventsourcing for Python?
The README says to install the stable distribution with pip and recommends using a Python virtual environment. The project metadata requires Python >=3.11.0.
How does eventsourcing work with an Aggregate and the @event decorator?
The README shows an Aggregate subclass whose methods use @event to name domain events such as Registered and TrickAdded. The application saves the aggregate through its repository, allowing the event store to record the changes.
Does eventsourcing support SQLite and other persistence modules?
The README documents SQLite configuration through the PERSISTENCE_MODULE and SQLITE_DBNAME environment variables. It also says that other persistence modules are available and that database recorders can be configured with environment variables.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pyeventsourcing-eventsourcing)Was this useful?