HexCore 
A reusable core for Python applications built on hexagonal architecture, DDD, CQRS and background tasks. HexCore ships the abstractions (entities, repositories, unit of work, buses) and the infrastructure every project otherwise rewrites: the SQL session layer, the FastAPI factories, the worker runner, the dynamic cron, identity, and the testing utilities.
The design goal is that the happy path takes zero configuration: create_app() with no
arguments gives you a usable app, init_engine() with no arguments gives you a
production-correct engine.
🇪🇸 ¿Preferís español? La documentación está completa en los dos idiomas: docs/es/.
# main.py — a complete HexCore app
from hexcore.fastapi import build_lifespan, create_app, SqlEngineStep
app = create_app(
lifespan=build_lifespan(SqlEngineStep()),
routers=[users_router, tickets_router],
)
# worker.py — the complete worker, with cron, mutual death and SIGTERM
import hexcore.cqrs as cqrs
await cqrs.run_procrastinate_worker(
procrastinate_app,
queues=["default", "reactive"],
scheduler=cqrs.DynamicScheduler(repo, enqueuer, lock_provider=lock),
on_startup=[lambda: cqrs.seed_cron_jobs(CRON_JOBS)],
)
📚 Documentation
→ docs/ — 🇬🇧 English · 🇪🇸 español
| English | Español | |
|---|---|---|
| Installation and extras | installation | instalacion |
| Quickstart | quickstart | inicio-rapido |
| Configuration | configuration | configuracion |
| SQL layer | sql | sql |
| Repositories and entities | repositories | repositorios |
| FastAPI utilities | fastapi | fastapi |
| CQRS architecture | cqrs | cqrs |
| Queues and workers | queues-and-workers | colas-y-workers |
| Scheduled tasks | cron | cron |
| Event Sourcing | event-sourcing | event-sourcing |
| Testing | testing | testing |
| CLI | cli | cli |
| Darwin (identity) | darwin/ | darwin/ |
| API reference | reference | referencia |
| Versions and migration | versions-and-migration | versiones-y-migracion |
| Typing | typing | tipado |
Installation
pip install hexcore
Requires Python ≥ 3.12. HexCore pulls in no heavy dependencies: everything that is not the core lives in extras, and the modules that need them only import them when you use them.
pip install "hexcore[api,sql,procrastinate]"
pip install "hexcore[darwin-sqlalchemy]"
pip install "hexcore[all]"
| Group | Extras |
|---|---|
| Core | api, sql, mongo, redis, rabbitmq, procrastinate, celery |
| Identity | darwin, darwin-sqlalchemy, darwin-beanie, darwin-magic-link, darwin-two-factor, darwin-oauth, darwin-impersonate, darwin-passkey, darwin-organization |
| Everything | all |
The full table, with what each one enables, is in installation · instalación.
import hexcore.cqrsworks with no extras at all: name resolution is lazy, sohexcore.cqrs.SqlAlchemyCronJobRepositoryonly requires[sql]at the moment you ask for it.
The four imports
There is one facade module per task. They re-export the public surface without moving anything: the long paths keep resolving to the same object.
import hexcore.fastapi as hx # create_app, build_lifespan, providers, middlewares, health
import hexcore.cqrs as cqrs # Command, Query, handlers, decorators, buses, worker, cron
import hexcore.sql as sql # init_engine, session_scope, uow_scope, Base, query DTOs
import hexcore.darwin as darwin # IdentityConfig, configure_identity, build_identity_router
The facades expose only the canonical names. The historical I* aliases were removed in
7.0 — see Removed API.
What you get, at a glance
| You need | API | Extra |
|---|---|---|
| A wired-up FastAPI app | hx.create_app(), hx.AppFeatures |
api |
| Orchestrated startup and shutdown | hx.build_lifespan() + steps |
api |
| SQL engine and sessions | sql.init_engine(), sql.PoolSettings |
sql |
| A session or UoW outside a request | sql.session_scope(), sql.uow_scope() |
sql |
| Health checks that actually probe | hx.register_health_routes() |
api |
| Rate limiting | hx.rate_limit() |
api |
| SSE / WebSocket / connection caps | hx.sse_stream(), hx.connection_slot() |
api |
| Commands, queries and events | cqrs.Command, cqrs.Query, cqrs.HandlerRegistry |
— |
| Running work in the background | cqrs.background_command, cqrs.background_task |
— |
| The worker entrypoint | cqrs.run_cqrs_worker(), cqrs.run_procrastinate_worker() |
— |
| Cron you can edit without a restart | cqrs.DynamicScheduler, cqrs.SqlAlchemyCronJobRepository |
sql |
| Distributed locks | cqrs.RedisLockProvider, cqrs.PostgresLockProvider |
redis / sql |
| Identity and authentication | darwin.configure_identity(), darwin.build_identity_router() |
darwin + storage |
| Testing all of the above | hexcore.testing |
— |
Darwin: the identity module
Registration, email verification, sign-in, sessions with rotating refresh, revocation, audited impersonation, and a plugin system that adds second factor, OAuth, magic links, passkeys and organizations without the core knowing about them.
from hexcore.darwin import (
IdentityConfig,
build_identity_router,
configure_identity,
identity_startup_steps,
)
from hexcore.fastapi import AppFeatures, SqlEngineStep, build_lifespan, create_app
configure_identity(IdentityConfig())
app = create_app(
features=AppFeatures(auth_context=True, csrf=True),
lifespan=build_lifespan(SqlEngineStep(), *identity_startup_steps()),
routers=[build_identity_router()],
)
⚠️ If you use SQL, the most important thing to read before deploying is the Alembic section:
storage · almacenamiento.
A plugin missing from your env.py makes alembic revision --autogenerate emit
op.drop_table for its tables.
Project templates (CLI)
hexcore init my_project --template hexagonal
hexcore init my_project --template vertical-slice
hexagonal→src/domain,src/application,src/infrastructure.vertical-slice→src/features,src/shared/{domain,application,infrastructure}.
Both generate a root config.py and leave Alembic configured. See
CLI · CLI.
Versions and support
| Series | Status | What it means |
|---|---|---|
| 9.x | ✅ Active | The only supported one. Receives features and fixes. Adds the event store and Event Sourcing, and leaves a single event bus port. |
| 8.x | ⛔ Deprecated | Ships Darwin. Migrating to 9.x is mechanical: the two deprecated names still resolve and warn. |
| 7.x | ⛔ Deprecated | Removes the pre-5.0 surface and fixes the CORS and rate-limiting defects. No Darwin: it shipped before the module landed on master. |
| 6.x | ⛔ Deprecated | No longer receives fixes. Contains the CORS and rate-limiting security defects fixed in 7.0, and the pre-5.0 aliases still present. |
| 5.x | ⛔ Deprecated | Same API surface as 6.x. |
| 4.x | ⛔ Deprecated | Partial application: missing the Celery event-loop fix, the facades, and the aligned documentation. |
| 3.x | ⛔ Deprecated | Partial application: has the P0/P1 fixes but none of the FastAPI factories. |
| 2.x | ⛔ Deprecated | Contains silent bugs fixed in 5.x: the worker re-enqueued instead of executing, the cron skipped or duplicated runs, and a Redis outage switched off the entire cron. |
| 1.x | ⛔ Deprecated | No support of any kind. |
Everything before 9.0 is deprecated. Migrate to 9.x. The detail of each series, the silent 2.x bugs and the step-by-step guides are in versions and migration · versiones y migración.
Removed API and its replacement
The v1/v2 aliases were deprecated since 5.0 — two full majors of notice — and were removed in 7.0. The replacement is mechanical: they are renames, not behavior changes.
| Removed in 7.0 (was v1/v2) | Use instead |
|---|---|
ICommandBus, IQueryBus, IEventBus |
AbstractCommandBus, AbstractQueryBus, AbstractEventBus |
ICommandHandler, IQueryHandler |
AbstractCommandHandler, AbstractQueryHandler |
IMiddleware |
AbstractMiddleware |
ISerializer |
AbstractSerializer |
IEventDispatcher |
EventBus |
EventBus.register() / .dispatch() |
EventBus.subscribe() / .publish() |
ServerConfig.event_dispatcher |
ServerConfig.event_bus |
SQLAlchemyCommonImplementationsRepo |
SqlAlchemyRepository |
BeanieODMCommonImplementationsRepo |
BeanieRepository |
NoSqlUnitOfWork |
BeanieUnitOfWork |
reset_sqlalchemy_engine() |
dispose_engine() |
MiddlewareConfig |
Removed in 3.0. It was dead code: never read. |
Passing event_dispatcher= to ServerConfig fails with an error that says what to use,
rather than being silently ignored: pydantic discards keyword arguments it does not know, and
keeping the default bus without noticing would surface much later as "my events never arrive".
If you are still on 6.x, run your tests with warnings visible to see what you have left to migrate:
python -m pytest -W "default::DeprecationWarning"
Contributing
-
Code of conduct — read the Code of Conduct before interacting.
-
Branches — fork and create a branch (
feat/name,fix/name,docs/name). -
Tests — every fix lands with at least one test that fails before and passes after:
uv sync --extra all --group dev uv run python -m pytest -q
CI fails if any test is skipped: a skip means an extra is missing, and we would be reporting green without having run half the suite.
-
Typecheck —
uv run pyright hexcore. The verdict comes from the ratchet, not the exit code: see typing · tipado. -
Style — PEP8. Comment the why, not the what.
-
Commits — Commitizen:
feat:,fix:,docs:,refactor:, and!for breaking changes. The version bump and the CHANGELOG are automatic on merge tomaster. -
PRs — describe the problem, the reproduction, the solution and why that option.
Full detail in CONTRIBUTING.md.
Project skills
There is a set of skills for extending HexCore in VS Code and compatible environments: HexCore Skills repository.
References
- docs/ — the complete documentation, in English and Spanish.
- docs/ARCHITECTURE_TYPING.md — type system and stubs.
- CHANGELOG.md — change history.
- CONTRIBUTING.md — collaboration guidelines.
- SECURITY.md — security policy.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hexcore-9.0.0.tar.gz.
File metadata
- Download URL: hexcore-9.0.0.tar.gz
- Upload date:
- Size: 764.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab047ae529e89c035624ac66973a598d34a45b540fc7e6a3d69d610aaac8aeae
|
|
| MD5 |
a368b62a4bce03b2dd099675dd040e71
|
|
| BLAKE2b-256 |
5f54a8f715236aab0bed5fd82932072a5d479df73ca6f9f1650ced60d9208af3
|
File details
Details for the file hexcore-9.0.0-py3-none-any.whl.
File metadata
- Download URL: hexcore-9.0.0-py3-none-any.whl
- Upload date:
- Size: 618.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c851b0fea811d7423458aa119bb326b863c243f94f00d1f0c41d8cb0ef335707
|
|
| MD5 |
ec263e572d839e9be8ae31391b6be725
|
|
| BLAKE2b-256 |
2180427a890d5a041ff9a9ee65fffe47a52f3387e4ac6020b7fcde0b2aaec2f4
|