<p align="center">
<img src="https://depeche-py.github.io/depeche-db/assets/logo-bg.png" width="200" />
</p>
# Depeche DB
A library for building event-based systems on top of PostgreSQL
[](https://github.com/depeche-py/depeche-db/actions/workflows/tests.yml)
[](https://pypi.python.org/pypi/depeche-db)
[](https://github.com/depeche-py/depeche-db)
[](https://depeche-py.github.io/depeche-db/)
[](https://github.com/depeche-py/depeche-db/blob/main/LICENSE)
---
**Documentation**: [https://depeche-py.github.io/depeche-db/](https://depeche-py.github.io/depeche-db/)
**Source code**: [https://github.com/depeche-py/depeche-db](https://github.com/depeche-py/depeche-db)
---
Depeche DB is modern Python library for building event-based systems
Key features:
* Message store with optimistic concurrency control & strong ordering guarantees
* Subscriptions with "at least once" semantics
* Parallel processing of (partitioned) subscriptions
* No database polling
## Requirements
Python 3.9+
SQLAlchemy 1.4 or 2+
PostgreSQL 12+
Psycopg (Version 2 >= 2.9.3 or Version 3 >= 3.1)
## Installation
```bash
pip install depeche-db
# OR
poetry add depeche-db
```
## Example
```python
import pydantic, sqlalchemy, uuid, datetime as dt
from depeche_db import (
MessageStore,
StoredMessage,
MessageHandler,
SubscriptionMessage,
)
from depeche_db.tools import PydanticMessageSerializer
DB_DSN = "postgresql://depeche:depeche@localhost:4888/depeche_demo"
db_engine = sqlalchemy.create_engine(DB_DSN)
class MyMessage(pydantic.BaseModel):
content: int
message_id: uuid.UUID = pydantic.Field(default_factory=uuid.uuid4)
sent_at: dt.datetime = pydantic.Field(default_factory=dt.datetime.utcnow)
def get_message_id(self) -> uuid.UUID:
return self.message_id
def get_message_time(self) -> dt.datetime:
return self.sent_at
message_store = MessageStore[MyMessage](
name="example_store",
engine=db_engine,
serializer=PydanticMessageSerializer(MyMessage),
)
message_store.write(stream="aggregate-me-1", message=MyMessage(content=2))
print(list(message_store.read(stream="aggregate-me-1")))
# [StoredMessage(message_id=UUID('...'), stream='aggregate-me-1', version=1, message=MyMessage(content=2, message_id=UUID('...'), sent_at=datetime.datetime(...)), global_position=1)]
class ContentMessagePartitioner:
def get_partition(self, message: StoredMessage[MyMessage]) -> int:
return message.message.content % 10
class MyHandlers(MessageHandler[MyMessage]):
@MessageHandler.register
def handle_message(self, message: SubscriptionMessage[MyMessage]):
print(message)
aggregated_stream = message_store.aggregated_stream(
name="aggregated",
partitioner=ContentMessagePartitioner(),
stream_wildcards=["aggregate-me-%"],
)
subscription = aggregated_stream.subscription(
name="example_subscription",
handlers=MyHandlers(),
)
aggregated_stream.projector.run()
subscription.runner.run()
# MyHandlers.handle_message prints:
# SubscriptionMessage(partition=2, position=0, stored_message=StoredMessage(...))
```
## Contribute
Contributions in the form of issues, questions, feedback and pull requests are
welcome. Before investing a lot of time, let me know what you are up to so
we can see if your contribution fits the vision of the project.
Raw data
{
"_id": null,
"home_page": null,
"name": "depeche-db",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "sqlalchemy, postgresql, event-driven, event store, message store, event sourcing",
"author": "Martin Vielsmaier",
"author_email": "martin@vielsmaier.net",
"download_url": "https://files.pythonhosted.org/packages/cf/ea/fce038e87796dc83f4c243b2414acdaec1d9302c8176c0fa3d21522f019a/depeche_db-0.12.1.tar.gz",
"platform": null,
"description": "\n\n<p align=\"center\">\n <img src=\"https://depeche-py.github.io/depeche-db/assets/logo-bg.png\" width=\"200\" />\n</p>\n\n# Depeche DB\n\nA library for building event-based systems on top of PostgreSQL\n\n[](https://github.com/depeche-py/depeche-db/actions/workflows/tests.yml)\n[](https://pypi.python.org/pypi/depeche-db)\n[](https://github.com/depeche-py/depeche-db)\n[](https://depeche-py.github.io/depeche-db/)\n[](https://github.com/depeche-py/depeche-db/blob/main/LICENSE)\n\n---\n\n**Documentation**: [https://depeche-py.github.io/depeche-db/](https://depeche-py.github.io/depeche-db/)\n\n**Source code**: [https://github.com/depeche-py/depeche-db](https://github.com/depeche-py/depeche-db)\n\n---\n\nDepeche DB is modern Python library for building event-based systems\n\nKey features:\n\n* Message store with optimistic concurrency control & strong ordering guarantees\n* Subscriptions with \"at least once\" semantics\n* Parallel processing of (partitioned) subscriptions\n* No database polling\n\n## Requirements\n\nPython 3.9+\nSQLAlchemy 1.4 or 2+\nPostgreSQL 12+\nPsycopg (Version 2 >= 2.9.3 or Version 3 >= 3.1)\n\n\n## Installation\n\n```bash\npip install depeche-db\n# OR\npoetry add depeche-db\n```\n\n## Example\n\n```python\nimport pydantic, sqlalchemy, uuid, datetime as dt\n\nfrom depeche_db import (\n MessageStore,\n StoredMessage,\n MessageHandler,\n SubscriptionMessage,\n)\nfrom depeche_db.tools import PydanticMessageSerializer\n\nDB_DSN = \"postgresql://depeche:depeche@localhost:4888/depeche_demo\"\ndb_engine = sqlalchemy.create_engine(DB_DSN)\n\n\nclass MyMessage(pydantic.BaseModel):\n content: int\n message_id: uuid.UUID = pydantic.Field(default_factory=uuid.uuid4)\n sent_at: dt.datetime = pydantic.Field(default_factory=dt.datetime.utcnow)\n\n def get_message_id(self) -> uuid.UUID:\n return self.message_id\n\n def get_message_time(self) -> dt.datetime:\n return self.sent_at\n\n\nmessage_store = MessageStore[MyMessage](\n name=\"example_store\",\n engine=db_engine,\n serializer=PydanticMessageSerializer(MyMessage),\n)\nmessage_store.write(stream=\"aggregate-me-1\", message=MyMessage(content=2))\nprint(list(message_store.read(stream=\"aggregate-me-1\")))\n# [StoredMessage(message_id=UUID('...'), stream='aggregate-me-1', version=1, message=MyMessage(content=2, message_id=UUID('...'), sent_at=datetime.datetime(...)), global_position=1)]\n\n\nclass ContentMessagePartitioner:\n def get_partition(self, message: StoredMessage[MyMessage]) -> int:\n return message.message.content % 10\n\n\nclass MyHandlers(MessageHandler[MyMessage]):\n @MessageHandler.register\n def handle_message(self, message: SubscriptionMessage[MyMessage]):\n print(message)\n\n\naggregated_stream = message_store.aggregated_stream(\n name=\"aggregated\",\n partitioner=ContentMessagePartitioner(),\n stream_wildcards=[\"aggregate-me-%\"],\n)\nsubscription = aggregated_stream.subscription(\n name=\"example_subscription\",\n handlers=MyHandlers(),\n)\n\naggregated_stream.projector.run()\nsubscription.runner.run()\n# MyHandlers.handle_message prints:\n# SubscriptionMessage(partition=2, position=0, stored_message=StoredMessage(...))\n\n```\n\n\n## Contribute\n\nContributions in the form of issues, questions, feedback and pull requests are\nwelcome. Before investing a lot of time, let me know what you are up to so\nwe can see if your contribution fits the vision of the project.\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A library for building event-based systems on top of PostgreSQL",
"version": "0.12.1",
"project_urls": {
"Changelog": "https://depeche-py.github.io/depeche-db/CHANGELOG/",
"Documentation": "https://depeche-py.github.io/depeche-db/",
"Homepage": "https://github.com/depeche-py/depeche-db",
"Repository": "https://github.com/depeche-py/depeche-db"
},
"split_keywords": [
"sqlalchemy",
" postgresql",
" event-driven",
" event store",
" message store",
" event sourcing"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "130f678e6d7143da6b9255fde681491868a2b3c79c2dace60b67201f4d559df7",
"md5": "052b4e4f59aaa5920db66183287283d5",
"sha256": "4eb1e5e9dd27791889a833722c80e0fdb9cf5b02f420495a7abde97085af0546"
},
"downloads": -1,
"filename": "depeche_db-0.12.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "052b4e4f59aaa5920db66183287283d5",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 45483,
"upload_time": "2025-07-21T12:18:53",
"upload_time_iso_8601": "2025-07-21T12:18:53.886367Z",
"url": "https://files.pythonhosted.org/packages/13/0f/678e6d7143da6b9255fde681491868a2b3c79c2dace60b67201f4d559df7/depeche_db-0.12.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "cfeafce038e87796dc83f4c243b2414acdaec1d9302c8176c0fa3d21522f019a",
"md5": "a47cffb86c676db0abaaea7cbd58f452",
"sha256": "beb77ac00a7cbe08a7b5f6213e4736c442048d8b07cd3345b2b644b7f7eba3dd"
},
"downloads": -1,
"filename": "depeche_db-0.12.1.tar.gz",
"has_sig": false,
"md5_digest": "a47cffb86c676db0abaaea7cbd58f452",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 36183,
"upload_time": "2025-07-21T12:18:55",
"upload_time_iso_8601": "2025-07-21T12:18:55.128476Z",
"url": "https://files.pythonhosted.org/packages/cf/ea/fce038e87796dc83f4c243b2414acdaec1d9302c8176c0fa3d21522f019a/depeche_db-0.12.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-21 12:18:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "depeche-py",
"github_project": "depeche-db",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "depeche-db"
}