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.1.0.tar.gz (13.6 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.1.0-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qx_db-0.1.0.tar.gz
  • Upload date:
  • Size: 13.6 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.1.0.tar.gz
Algorithm Hash digest
SHA256 72ad7b6b345c63ad58b45c7a8d025fc8f2b576f8ecf6357dee0019a9b5ddd023
MD5 3754e48433db2b7c1e0fbcda2d798fd9
BLAKE2b-256 e801ada27b0e44b8c7037aeb58dc198711c204432b5cf10db92ef805542f6484

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qx_db-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for qx_db-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f3071c512421d085af557a2b6f70c587aa12f227a1d39a568f89297abf4b64fc
MD5 927e2a52d10f05e4529ea7f5520e39ed
BLAKE2b-256 a8de28182508c5acfcbd3eaa755a649d593124ccc95366715d413404458bc7aa

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