Skip to main content

Qx database layer: SQLAlchemy 2 async, repositories, unit of work, transactional outbox

Project description

qx-db

Database layer for the Qx framework — SQLAlchemy 2 async, a generic Repository[TEntity], UnitOfWork with domain-event routing, transactional outbox, and cursor/offset pagination.

What lives here

  • qx.db.Repository[TEntity] — generic async CRUD with optimistic concurrency (version column), soft-delete, tenant filtering, and allow-listed filter/sort fields.
  • qx.db.UnitOfWork — wraps a SQLAlchemy session; commits the aggregate write and the outbox INSERT in one transaction, then drains and dispatches domain events.
  • qx.db.OutboxRecorder / DefaultOutboxRecorder — persists IntegrationEvent payloads to qx_outbox_events for reliable delivery.
  • qx.db.SessionFactory — factory for AsyncSession instances; injected into repositories.
  • qx.db.make_metadata / make_registry — SQLAlchemy MetaData and registry helpers for imperative mapping (no declarative base).
  • qx.db.standard_audit_columns / uuid_column / jsonb_column — column helpers for consistent schema conventions.
  • qx.db.build_cursor_page / encode_cursor / decode_cursor — opaque cursor pagination utilities.
  • qx.db.include_outbox_table — attaches the qx_outbox_events table to your MetaData.

Defining a repository

from qx.db import Repository
from sqlalchemy import Table

class UserRepository(Repository[User]):
    entity_cls = User
    table: Table  # set to your SQLAlchemy Table at class or instance level
    filterable_fields = {"email", "name", "is_active"}
    sortable_fields = {"created_at", "email"}
    tenanted = False  # set True for multi-tenant tables

Unit of Work

from qx.db import UnitOfWork

class CreateUserHandler:
    def __init__(self, uow: UnitOfWork) -> None:
        self._uow = uow

    async def handle(self, cmd: CreateUserCommand) -> Result[UserDto]:
        async with self._uow.begin() as ctx:
            user_result = User.register(cmd.email, cmd.name)
            user = unwrap(user_result)
            await ctx.users.add(user)
        # commit + outbox INSERT + event dispatch all happened above
        return Result.success(UserDto.from_domain(user))

Design rules

  • Optimistic concurrencysave() uses WHERE id = ? AND version = ?; returns ConflictError (not an exception) on mismatch.
  • Soft-delete by defaultlist() and get() exclude rows where deleted_at IS NOT NULL unless include_deleted=True.
  • Allow-listed filtersfilterable_fields and sortable_fields are class-level sets; querying an unlisted field raises ValueError to prevent controller logic leaking into SQL.
  • Imperative mapping — domain entities are plain dataclasses with no SQLAlchemy decorators. The mapping lives in the infrastructure layer, not the domain.
  • UUID v7uuid_column() defaults to UUID v7 primary keys for sequential B-tree index locality.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qx_db-1.1.0.tar.gz (23.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qx_db-1.1.0-py3-none-any.whl (26.1 kB view details)

Uploaded Python 3

File details

Details for the file qx_db-1.1.0.tar.gz.

File metadata

  • Download URL: qx_db-1.1.0.tar.gz
  • Upload date:
  • Size: 23.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qx_db-1.1.0.tar.gz
Algorithm Hash digest
SHA256 577e47a87872a1758469a99f5e2a67731a6a9a1e1dd59c83f83afc056bde8ad5
MD5 f835013c90445b57eff808c27522abe2
BLAKE2b-256 d4769630c643c0adc940eb80b732e3f9c267458008d95ab81960673832245234

See more details on using hashes here.

File details

Details for the file qx_db-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: qx_db-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 26.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qx_db-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6f00d64a987d4d200b4a829b468a935391d8b7cfc274d29243ec2bc16fe8db69
MD5 87f7ba19750f1951622d7e2dedb141e4
BLAKE2b-256 7b1913cee3cc7fc04825c7a05d034dc33f45674fb29647a6660d28e89b6561f0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page