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-0.2.0.tar.gz (21.9 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-0.2.0-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qx_db-0.2.0.tar.gz
  • Upload date:
  • Size: 21.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for qx_db-0.2.0.tar.gz
Algorithm Hash digest
SHA256 528aac6a74334e7bd57b29ea4b9766a4ef2c1403ec2d84e2fa0b7e8a440aed4e
MD5 c2a5f3e31ee78abd6e93a0abb87e8626
BLAKE2b-256 16c37c6b7e21bf90f44384726a93357a953a122fc7f662c8c1e1c3ed92a199f5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qx_db-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 24.5 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-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b33bf53d3c380af2486d2942fd5018af3ee15d4776eefafe4a4879bb0201e862
MD5 18f619028f53cad08e100fdaecdd0b14
BLAKE2b-256 3d53cfc8cc9306902d676e72f2f7252dbdb33780a3d1ad8115dd2c4266c1ef51

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