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.0.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.0.0-py3-none-any.whl (26.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qx_db-1.0.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.0.0.tar.gz
Algorithm Hash digest
SHA256 4efae90b24f240eb51532b12ef40ee8391a423efa78999ce66671091f1d2bb81
MD5 959f11015250ba0bfc5ae5ea38047ab1
BLAKE2b-256 2731d4901d9d3d2082c49fb06f2f09e270257b9dd180a25af40afe8e0089e2a9

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qx_db-1.0.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.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1d9424db56e4a7ea13607aae81cf18b995917139d5257a0409b74f0fabf5aa69
MD5 e2ef81cb1f7d32f53dfc69934b304a2b
BLAKE2b-256 5d07a918e368ad59ea06df40b91bc8a8bafc7aea65325f341fb5bca8dc22a9ae

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