Metadata-Version: 2.4
Name: domainkit
Version: 0.7.0
Summary: A Django-style framework for domain-driven FastAPI services.
License-Expression: MIT
License-File: LICENSE
Keywords: ddd,domain-driven-design,fastapi,sqlalchemy,outbox,framework
Author: karim magdy
Author-email: karimhady111@gmail.com
Requires-Python: >=3.13,<4
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Provides-Extra: argon2
Provides-Extra: immudb
Provides-Extra: jwt
Provides-Extra: metrics
Provides-Extra: mfa
Provides-Extra: postgres
Provides-Extra: rabbitmq
Provides-Extra: redis
Provides-Extra: sqlite
Provides-Extra: testing
Provides-Extra: tracing
Provides-Extra: tzdata
Provides-Extra: uploads
Provides-Extra: webhooks
Requires-Dist: aio-pika (>=10.1.0,<11.0.0) ; extra == "rabbitmq"
Requires-Dist: aiosqlite (>=0.22.1,<0.23.0) ; extra == "sqlite"
Requires-Dist: alembic (>=1.20.0,<2.0.0)
Requires-Dist: argon2-cffi (>=25.1.0,<26.0.0) ; extra == "argon2"
Requires-Dist: asyncpg (>=0.31.0,<0.32.0) ; extra == "postgres"
Requires-Dist: cryptography (>=50.0.2,<51.0.0) ; extra == "mfa"
Requires-Dist: cryptography (>=50.0.2,<51.0.0) ; extra == "webhooks"
Requires-Dist: fastapi (>=0.141.1,<0.142.0)
Requires-Dist: httpx2 (>=2.13.0,<3.0.0) ; extra == "testing"
Requires-Dist: httpx2 (>=2.13.0,<3.0.0) ; extra == "webhooks"
Requires-Dist: immudb-py (>=1.5.0,<1.6.0) ; extra == "immudb"
Requires-Dist: opentelemetry-api (>=1.45.1,<2.0.0) ; extra == "tracing"
Requires-Dist: prometheus-client (>=0.26.0,<0.27.0) ; extra == "metrics"
Requires-Dist: pydantic (>=2.14.1,<3.0.0)
Requires-Dist: pyjwt[crypto] (>=2.10.1,<3.0.0) ; extra == "jwt"
Requires-Dist: pytest (>=9.1.1,<10.0.0) ; extra == "testing"
Requires-Dist: python-dotenv (>=1.2.4,<2.0.0)
Requires-Dist: python-multipart (>=0.0.32,<0.1.0) ; extra == "uploads"
Requires-Dist: redis (>=8.1.0,<9.0.0) ; extra == "redis"
Requires-Dist: sqlalchemy[asyncio] (>=2.0.54,<2.1)
Requires-Dist: tzdata (>=2025.2) ; extra == "tzdata"
Requires-Dist: uvicorn[standard] (>=0.54.0,<0.55.0)
Project-URL: Changelog, https://github.com/karimmattar/domainkit/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/karimmattar/domainkit/blob/main/docs/reference.md
Project-URL: Homepage, https://github.com/karimmattar/domainkit
Project-URL: Issues, https://github.com/karimmattar/domainkit/issues
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/karimmattar/domainkit/main/docs/assets/domainkit-banner.jpg" alt="domainkit, a domain-driven FastAPI framework" width="720">
</p>

# domainkit

A Django-style framework for domain-driven FastAPI services. It provides:

- settings modules and installed domains
- `manage.py` commands and migrations
- DDD building blocks, with the layer rules between them enforced
- several databases: domains routed to their own, sharding by key,
  PostgreSQL range partitioning, and immudb ledgers for evidence
- caches in memory or in Redis and Valkey (one server, Sentinel, or
  Cluster), with stampede protection, and rate limits shared by every
  replica, globally or per route
- answers in the caller's language (English and Arabic ship), times shown
  in the business's time zone, and numbers and dates in local formats
- file storage and uploads checked by their content, under names the
  storage makes
- background work through a transactional outbox, run by polling workers or
  through RabbitMQ, with queues for separate worker pools
- users, groups, and permissions per content type, as in Django's
  `contrib.auth`: password hashing and rules, backends, object permissions,
  and a swappable user model; token or JWT authentication, and route rules
  as in Django REST framework
- optional apps for reliable messaging, audit trails, idempotent requests,
  tamper-evident evidence, signed outgoing webhooks, feature flags, and privacy
  requests (export, erasure, and retention)

```bash
pip install "domainkit[postgres]"     # extras: postgres, sqlite, argon2, jwt, mfa, immudb, redis, rabbitmq, metrics, tracing, uploads, testing, webhooks, tzdata
```

## Documentation

- [docs/reference.md](https://github.com/karimmattar/domainkit/blob/main/docs/reference.md): settings, middleware, errors,
  the API surface (OpenAPI, versions, deprecations), observability,
  testing, commands, and extension points
- [docs/databases.md](https://github.com/karimmattar/domainkit/blob/main/docs/databases.md): several databases, routing,
  sharding, partitioning, and ledgers
- [docs/queries.md](https://github.com/karimmattar/domainkit/blob/main/docs/queries.md): querying repositories,
  pages, filters, soft delete, row locks, and savepoints
- [docs/resources.md](https://github.com/karimmattar/domainkit/blob/main/docs/resources.md): aggregates over REST:
  resource routes, commands and queries, derived schemas, partial changes,
  ETags and conditional writes, field policies, nesting, the generators,
  and the generic admin
- [docs/cache.md](https://github.com/karimmattar/domainkit/blob/main/docs/cache.md): caches in memory, Redis, and Valkey,
  and shared rate limits
- [docs/contrib.md](https://github.com/karimmattar/domainkit/blob/main/docs/contrib.md): messaging (and workers on RabbitMQ),
  webhooks, audit and records' history, idempotency, evidence, and
  feature flags
- [docs/auth.md](https://github.com/karimmattar/domainkit/blob/main/docs/auth.md): users, groups, permissions, content
  types, passwords, backends, tokens, route rules, and the admin API
- [docs/mail.md](https://github.com/karimmattar/domainkit/blob/main/docs/mail.md): email, mail templates, text messages,
  and push notifications, sent through the outbox
- [docs/i18n.md](https://github.com/karimmattar/domainkit/blob/main/docs/i18n.md): responses in the caller's language,
  time zones, and number and date formats
- [docs/uploads.md](https://github.com/karimmattar/domainkit/blob/main/docs/uploads.md): file storages, receiving uploads,
  and serving files
- [docs/privacy.md](https://github.com/karimmattar/domainkit/blob/main/docs/privacy.md): exporting and erasing a person's
  data, and retention
- [docs/testing.md](https://github.com/karimmattar/domainkit/blob/main/docs/testing.md): the pytest plugin: test databases,
  sync and async clients, factories, a clock to freeze, and query counts
- [docs/versioning.md](https://github.com/karimmattar/domainkit/blob/main/docs/versioning.md): compatibility, deprecations, and releases
- [docs/adr/](https://github.com/karimmattar/domainkit/tree/main/docs/adr): the decisions behind the design
- [CHANGELOG.md](https://github.com/karimmattar/domainkit/blob/main/CHANGELOG.md) and [SECURITY.md](https://github.com/karimmattar/domainkit/blob/main/SECURITY.md)

## Quick start

```bash
python3.13 -m venv .venv && . .venv/bin/activate
pip install "domainkit[postgres,sqlite,testing]"
domainkit init billingAPI billing     # the name sets the variable prefix: BILLING_API_*
cd billing
python manage.py check
python manage.py startdomain invoices --aggregate Invoice   # installs and mounts it
python manage.py makemigrations -m "create invoices" && python manage.py migrate
python manage.py createsuperuser
pytest
python manage.py runserver            # http://127.0.0.1:8000/docs
```

`domainkit init` creates a project that passes `check` and its tests as it is.
Users, groups, and permissions are installed, requests are authenticated,
and every route must say who may call it: generated domains need their
model permissions, and `/auth` and `/admin/auth` log users in and manage
them ([docs/auth.md](https://github.com/karimmattar/domainkit/blob/main/docs/auth.md)).


```text
billing/
  pyproject.toml          the name, dependencies, and the pytest plugin
  manage.py
  .env                    selects config.settings.local on this machine
  .gitignore
  src/config/settings/    base, local, test, and production settings
  src/config/urls.py      the root router: mount each domain's router here
  src/config/asgi.py      the application, for uvicorn
  src/domains/            the bounded contexts
  tests/test_health.py
```

The production settings read `BILLING_API_ALLOWED_HOSTS`,
`BILLING_API_DATABASE_URL`, and `BILLING_API_TRUSTED_PROXIES` (the load
balancers' networks, or empty) from the environment, and the deployment
checks refuse to start without them, or without verified TLS to the database. They
also turn the API docs off, send HSTS for a year, and limit each caller to
300 requests a minute; `BILLING_API_API_DOCS`,
`BILLING_API_SECURE_HSTS_SECONDS`, and `BILLING_API_RATE_LIMIT` change that.

## Developing domainkit

```bash
poetry install --all-extras            # Python 3.13
pre-commit install                     # after: pipx install pre-commit

poetry run ruff check . && poetry run ruff format --check . && poetry run mypy
poetry run pytest --cov                # random order; coverage gate in pyproject.toml
poetry run mutmut run "domainkit.http*" && poetry run mutmut results
```

Without servers, the database tests use SQLite, the Redis and RabbitMQ
tests use in-memory fakes, and the tests that need PostgreSQL, immudb, or a
real Redis, Valkey, or RabbitMQ are skipped. Point them at throwaway servers to run
them; each test cleans up after itself:

```bash
docker run -d --rm --name domainkit-postgres -e POSTGRES_PASSWORD=postgres \
  -p 127.0.0.1:55432:5432 postgres:18-alpine
docker run -d --rm --name domainkit-redis -p 127.0.0.1:56379:6379 redis:8-alpine
DOMAINKIT_TEST_POSTGRES_URL=postgresql+asyncpg://postgres:postgres@127.0.0.1:55432/postgres \
DOMAINKIT_TEST_REDIS_URL=redis://127.0.0.1:56379/0 \
  poetry run pytest
```

The other variables (immudb, Redis over TLS, Valkey, RabbitMQ) are listed
at the top of `domainkit_tests/test_immudb.py`, `test_redis.py`, and
`test_rabbitmq.py`. Each run must cover
98%; CI also combines the runs with and without servers, which must cover
100%. To check that locally, run `pytest --cov` once without and once with
the servers, each with its own `COVERAGE_FILE`, then `coverage combine` and
`coverage report --fail-under=100`.

CI (`.github/workflows/ci.yml`) runs on every push and pull request:

- lint and strict typing
- the tests on Python 3.13 and 3.14
- the tests again against PostgreSQL 18, immudb with TLS and a signing key,
  Redis 8 (plain and TLS), Valkey 9, and RabbitMQ 4 (plain and TLS); no test
  may be skipped there
- coverage of both runs combined, which must be 100%
- a wheel build, installed and imported in a clean environment
- pip-audit, and a gitleaks scan of the whole history

A full mutation testing run happens weekly. Releases are published to PyPI
from a version tag; see [docs/versioning.md](https://github.com/karimmattar/domainkit/blob/main/docs/versioning.md).

## License

MIT; see [LICENSE](https://github.com/karimmattar/domainkit/blob/main/LICENSE).

