Change the database
Another SQL database: change the URL
DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/db # asyncpg ships in the `postgres` extra
DATABASE_URL=mysql+aiomysql://user:pass@host:3306/db # uv add aiomysql
DATABASE_URL=sqlite+aiosqlite:///./dev.db # default
The SQLAlchemy adapter, the migrations and the tests are dialect-agnostic
(render_as_batch=True in alembic/env.py keeps SQLite happy with ALTER TABLE).
CI runs the integration tests and migrations on PostgreSQL 17.
No database: memory://
DATABASE_URL=memory://
bootstrap wires the in-memory adapters; nothing is persisted between restarts. Good for
demos, front-end development against a fake backend, and the API tests.
A different technology (MongoDB, DynamoDB, an HTTP service...)
The domain and application rings do not change. You write one adapter and one line of wiring:
-
tournaments/infrastructure/mongo.py:class MongoTournamentRepository:def __init__(self, collection: AsyncIOMotorCollection) -> None: ...async def add(self, tournament: Tournament) -> None: ...async def get(self, tournament_id: TournamentId) -> Tournament | None: ...async def save(self, tournament: Tournament) -> None: ...async def list(self, *, limit: int, offset: int) -> Sequence[Tournament]: ...mypy checks it against the
TournamentRepositoryProtocol - no inheritance needed. -
A mapping between the aggregate and the document, next to it (
mongo_mapping.py). -
In
bootstrap/features/tournaments.py, replace the SQLAlchemy branch ofwire_http(or add a third branch on a setting). Whatever owns the connection lifecycle goes inlifespan. -
Copy the repository contract test of the example (
tests/tournaments/test_repository_contract.py) and add the new adapter to itsparams. The assertions stay identical: that is the contract every adapter must satisfy.
Swapping the ORM itself
shared/infrastructure/database.py (engine, session, transaction()) and
tournaments/infrastructure/sqlalchemy/ are the only places that import SQLAlchemy;
alembic/ is the only place that depends on it for migrations. Replace those, keep
everything else.