diff --git a/README.md b/README.md
index e806af4..c288804 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
- A composable architectural foundation for building scalable applications on top of FastAPI.
- Standardize your structure, manage atomic transactions, and protect sensitive data β without sacrificing FastAPI's flexibility.
+ An architectural chassis for building modular applications on top of FastAPI.
+ Structuring transactional boundaries, constructor dependency injection, and field-level access controls while preserving native FastAPI semantics.
@@ -29,51 +29,49 @@
## π§ Overview
-**FastAPI is an exceptional microframework for HTTP routing, request parsing, and asynchronous I/O.**
+FastAPI provides an efficient, ergonomic foundation for HTTP handling, asynchronous I/O, and data validation using Pydantic. However, as codebases grow beyond simple APIs into larger services or modular monoliths, engineering teams frequently face common structural challenges:
-However, as applications grow into medium and large systems, teams inevitably encounter an architectural gap:
-* Database sessions leak across asynchronous tasks or background jobs.
-* Business workflows spanning multiple repositories lack reliable, all-or-nothing transaction boundaries.
-* Teams end up writing dozens of nearly identical DTO schemas just to hide internal fields (like profit margins, supplier terms, or sensitive IDs) from different user roles.
-* Domain modules become tightly coupled through circular imports and tangled dependency trees.
+* **Session Lifetime & Leaks:** Database sessions can inadvertently leak across concurrent coroutines or terminate prematurely during background tasks.
+* **Transaction Coordination:** Workflows that update multiple repositories often lack a unified, atomic transaction boundary, risking partial writes when a middle step fails.
+* **Schema Proliferation:** Writing and maintaining separate Pydantic models (`UserPublic`, `UserInternal`, `UserAdminResponse`) solely to conceal specific fields across user roles introduces repetitive boilerplate.
+* **Module Coupling:** Large domain modules frequently develop tangled dependency graphs and circular imports when interacting with shared databases.
-Heavy enterprise frameworks solve these challenges by enforcing rigid conventions, proprietary ORMs, and heavy abstractions that eliminate your freedom.
+Full-stack monolithic frameworks solve these issues by introducing comprehensive conventions, coupled ORMs, and centralized registries. While effective, this approach can reduce architectural flexibility.
-**ZCore is the middle ground:**
-ZCore provides a structured, modular layer that sits directly on top of standard FastAPI, SQLAlchemy 2.0, and Pydantic V2. It introduces proven architectural patterns (Unit of Work, IoC Dependency Injection, Field-Level Security, and Domain Event Dispatching) as **composable building blocks**.
+**ZCore is designed as an intermediate layer.** It does not hide FastAPI or replace its router. Instead, it provides a set of decoupled architectural primitivesβsuch as a re-entrant Unit of Work, constructor-based Inversion of Control (IoC), context-aware schema filtering (`Zchema`), and lifecycle pluginsβbuilt on top of standard **FastAPI**, **SQLAlchemy 2.0 (Async)**, and **Pydantic V2**.
-> **Your code remains 100% standard FastAPI.** Adopt ZCore piece by piece where it solves real problems, or use the full architectural stack β with zero vendor lock-in.
+You can adopt individual components where they solve concrete architectural needs or use the structured chassis as a cohesive pattern for modular application design.
---
-## ποΈ Reference Showcase: ZShop
+## ποΈ Reference Implementation: ZShop
-To see ZCore's architectural concepts in action before writing a single line of code, explore **[ZShop](https://github.com/Baseryn/zshop)** β the official Modular Monolith e-commerce reference implementation:
+To examine how these architectural patterns interact in an end-to-end codebase, refer to **[ZShop](https://github.com/Baseryn/zshop)**, a modular monolith reference application:
-* **Interactive Live Inspector Dock:** Monitor active request context, correlation IDs (`x-request-id`), execution latencies, and real-time SSE stream events.
-* **1-Click RBAC Persona Switcher:** Switch between SuperAdmin, Store Manager, Customer, and Guest to watch confidential data fields automatically vanish from responses via `Zchema`.
-* **Atomic Rollback Demonstration:** An interactive deficit checkout button verifying that stock adjustments and order persistence roll back atomically when any invariant fails.
-* **Decoupled Architecture:** Strict inter-module communication using Python Protocols (`ProductContract`) without cross-domain database coupling.
+* **Context & Security Inspection:** Demonstrates active correlation IDs (`x-request-id`), execution profiling, and role-based data projection.
+* **Role-Based Schema Pruning:** Displays how confidential attributes (such as supplier costs) are dynamically excluded based on the authenticated context via `Zchema`.
+* **Atomic Rollback Demonstration:** Validates multi-repository rollback guarantees by simulating invariant violations during order checkout.
+* **Contract-Driven Decoupling:** Demonstrates cross-domain communication via Python `Protocols` (`ProductContract`) rather than direct database model imports.
-π **[Explore the ZShop Showcase Repository](https://github.com/Baseryn/zshop)**
+π **[Explore the ZShop Reference Codebase](https://github.com/Baseryn/zshop)**
---
-## βοΈ Why ZCore? (An Honest Comparison)
+## βοΈ Architectural Comparison
-| Architectural Concern | Plain FastAPI | Full-Stack Monoliths (e.g. Django) | With ZCore on FastAPI |
+| Area | Plain FastAPI | Heavy Monoliths (e.g., Django) | FastAPI with ZCore |
| :--- | :--- | :--- | :--- |
-| **Application Philosophy** | Minimalist microframework; no opinion on database, folders, or architecture. | Opinionated, batteries-included; tightly coupled to its own ORM and template engine. | **Composable layer;** provides enterprise architecture patterns while keeping FastAPI completely standard. |
-| **Role-Based Data Exposure** | Writing multiple Pydantic models per role (`UserPublic`, `UserAdmin`) or manual `if/else` checks. | Model-level permissions or custom serializer logic. | **Single-schema context shielding (`Zchema`);** forbidden fields are pruned dynamically from validation, serialization, and OpenAPI. |
-| **Multi-Repository Operations** | Scattered `session.commit()` calls; high risk of partial writes on failure. | Managed transaction blocks (`transaction.atomic()`). | **Re-entrant `UnitOfWork`;** coordinated all-or-nothing boundaries with domain events deferred until physical commit succeeds. |
-| **Background Processing** | Passing request sessions to background tasks often leads to `Session is closed` errors. | Requires external queue workers (Celery, RQ) even for simple background jobs. | **Dedicated `@background_task`;** automatically isolates DB sessions and IoC lifecycles from completed HTTP requests. |
-| **Domain Modularity** | Standard APIRouters; easily slips into spaghetti code and circular imports at scale. | Django Apps with rigid settings and global app registries. | **DAG-driven `Plugin` system;** topological startup sorting and pure Python Protocol contracts for zero-coupling. |
-| **Dataset Pagination** | Manual `offset / limit` SQL slicing that degrades on large tables. | Built-in offset paginators. | **Keyset Cursor Pagination (Base64);** zero-drift pagination and dynamic AST-like JSON search engine. |
-| **Testing Isolation** | Manual DB cleanup routines or slow schema recreation per test file. | TransactionTestCase rolling back transactions per test. | **Zero-side-effect `ZTestClient`;** automated savepoint rollbacks, context mocking, and container sandboxing. |
+| **Philosophy** | Minimalist microframework; architectural organization is left entirely to the developer. | Batteries-included, opinionated; coupled to built-in ORM and global application registries. | **Composable layer;** provides structured architectural patterns while keeping routers and schemas standard. |
+| **Field-Level Security** | Manual `if/else` checks or maintaining distinct Pydantic DTOs for each access level. | Model-level field permissions or specialized serializer layers. | **Context-aware pruning (`Zchema`);** dynamically filters restricted fields across validation, serialization, and dynamic schema introspection. |
+| **Transaction Management** | Manual `session.commit()` calls across routes and services; potential for partial writes. | Built-in transaction blocks (`transaction.atomic()`). | **Re-entrant `UnitOfWork`;** depth-aware coordination with domain events buffered until root commit succeeds. |
+| **Background Processing** | Request-scoped sessions passed to `BackgroundTasks` often encounter closed session errors. | Relies on external task queue workers (e.g., Celery) even for simple background steps. | **Scoped `@background_task`;** isolates session allocation and IoC lifecycles from completed request scopes. |
+| **Modular Domain Design** | Standard `APIRouter` instances; vulnerable to circular imports as features expand. | Strict app layout and global settings configuration. | **DAG-driven `Plugin` system;** topological startup sorting and pure Python Protocol contracts for interface decoupling. |
+| **Dataset Pagination** | Manual `offset / limit` SQL slicing, which may suffer performance degradation on large tables. | Built-in offset paginators. | **Keyset Cursor Pagination;** compound keyset traversal with URL-safe base64 cursors, alongside dynamic JSON filtering. |
+| **Test Isolation** | Manual database cleanup or recreating schema fixtures between test runs. | Transactional test cases rolling back writes per test method. | **Rollback harness via `ZTestClient`;** automated savepoint rollbacks, container isolation, and context mocking. |
---
-## π Quick Start (in Under 60 Seconds)
+## π Quick Start
### 1. Installation
@@ -81,21 +79,21 @@ To see ZCore's architectural concepts in action before writing a single line of
pip install "fastapi-zcore-framework[all]"
```
-### 2. Scaffold a New Project
+### 2. Initialize a Project
-The interactive `zc` CLI configures your environment, sets up a virtual environment (`uv` or `pip`), and provisions your database driver:
+The interactive `zc` CLI configures your workspace, sets up a virtual environment (`uv` or `pip`), and bootstraps the appropriate asynchronous database driver:
```bash
-# Interactive mode (recommended)
+# Interactive setup
zc init core_api && cd core_api
-# Generate your first domain module
+# Scaffold a domain module
zc startapp tasks
```
### 3. Define Your Domain Model
-Open `tasks/models.py` (standard SQLAlchemy 2.0):
+In `tasks/models.py` (standard SQLAlchemy 2.0):
```python
import uuid
@@ -116,33 +114,35 @@ class Task(Base, SoftDeleteMixin):
zc run
```
-Your API is running at `http://127.0.0.1:8000` with **8 production-ready endpoints** out of the box:
-* `POST /tasks/` β Create entity
-* `GET /tasks/{id}` β Fetch single record (with automatic PK type inspection)
-* `GET /tasks/` β List records (with cursor or offset pagination)
-* `POST /tasks/search` β Dynamic nested JSON filtering engine
-* `POST /tasks/lookup` β Lightweight field-projected endpoint for dropdowns
-* `PUT /tasks/{id}` & `PATCH /tasks/{id}` β Full and partial updates
-* `DELETE /tasks/{id}` β Soft delete (or permanent purge via `?force=true`)
-* `GET /tasks/?schema=true` β Dynamic role-aware frontend form schema
+The application starts at `http://127.0.0.1:8000` with 8 standard endpoints managed by `BaseRouter`:
+* `POST /tasks/` β Entity creation
+* `GET /tasks/{id}` β Single-record retrieval with primary key inspection
+* `GET /tasks/` β List retrieval supporting offset or keyset cursor pagination
+* `POST /tasks/search` β Dynamic nested JSON filtering and sorting
+* `POST /tasks/lookup` β Lightweight, column-projected endpoint for relational references
+* `PUT /tasks/{id}` & `PATCH /tasks/{id}` β Complete and partial updates
+* `DELETE /tasks/{id}` β Soft delete (or physical purge via `?force=true`)
+* `GET /tasks/_schema` β Role-aware schema introspection (all actions or filtered via `?action=create|update|view|lookup`)
---
-## ποΈ Core Architectural Patterns
+## ποΈ Core Architectural Components
-1. Context Shielding & Role-Based Field Pruning (Zchema)
+1. Context Shielding & Field-Level Access Control (Zchema)
-### The Problem
-In enterprise applications, different roles require different views of the same data. A Customer must not see wholesale cost prices or internal supplier notes, while a Store Manager requires full financial transparency. Maintaining separate DTOs (`ProductPublic`, `ProductManager`, `ProductCreate`, `ProductAdminUpdate`) creates boilerplate and maintenance overhead.
+### Problem
+Managing multiple versions of the same schema (`ProductCustomerView`, `ProductInternalView`, `ProductAdminUpdate`) to hide sensitive attributes (such as purchase cost or internal remarks) introduces redundancy and increases the risk of accidental data leaks.
-### The ZCore Solution
-Define your model once inheriting from `Zchema`. When a user's security context marks a field as restricted, ZCore automatically prunes it across **input parsing** (preventing mass assignment), **response serialization** (preventing data leakage), and **OpenAPI schema generation**:
+### Solution
+Define the schema once by inheriting from `Zchema`. When fields are marked as restricted within the current request context (`ctx.restricted_fields`), ZCore prunes them across:
+1. **Input validation:** Silently discards restricted fields to prevent mass assignment.
+2. **Output serialization:** Removes restricted attributes before JSON encoding.
+3. **Schema introspection:** Omits restricted properties dynamically when querying the `/_schema` endpoint.
```python
import uuid
from decimal import Decimal
-from pydantic import Field
from zcore import Zchema
class ProductResponse(Zchema):
@@ -151,51 +151,50 @@ class ProductResponse(Zchema):
id: uuid.UUID
name: str
price: Decimal
- cost_price: Decimal | None = None # π Sensitive business margin
- supplier_notes: str | None = None # π Internal agreement details
+ cost_price: Decimal | None = None # Internal margin
+ supplier_notes: str | None = None # Vendor agreement notes
```
-* **When a Customer views the product:** `cost_price` and `supplier_notes` are completely omitted from the JSON output. ZCore automatically attaches a `Vary: Authorization, Cookie` header to protect downstream CDNs from cache poisoning.
-* **When a Manager views the product:** All fields are returned transparently.
+When restricted fields are pruned from a response, ZCore appends a `Vary: Authorization, Cookie` header to prevent shared HTTP caches or CDNs from caching privileged data representations for unprivileged clients.
2. Atomic Unit of Work & Deferred Domain Events (UnitOfWork)
-### The Problem
-In distributed or multi-repository architectures, business actions involve multiple persistence steps (e.g., deducting inventory, recording a purchase order, and generating an invoice). If step 2 fails, step 1 must be reverted. Furthermore, if you send an email or emit a domain event *before* the database commit succeeds, a subsequent failure leaves external systems out of sync.
+### Problem
+When business operations touch multiple repositories, issuing separate commits introduces the risk of partial database writes if an intermediate step fails. Additionally, publishing domain events (or sending notifications) before a database transaction physically commits can lead to state inconsistency if the transaction ultimately rolls back.
-### The ZCore Solution
-ZCore implements a re-entrant, depth-aware `UnitOfWork`. Nested transactions safely flush changes to savepoints without prematurely committing, while domain events are buffered and dispatched **only after the root commit succeeds**:
+### Solution
+ZCore implements a depth-aware, re-entrant `UnitOfWork`. Nested scopes flush changes safely without premature commits, while domain events remain buffered and are dispatched only after the root commit succeeds:
```python
from zcore import UnitOfWork
async with UnitOfWork(session=self.session, dispatcher=self.dispatcher) as uow:
- # 1. Adjust inventory
+ # 1. Update inventory
await self.inventory_service.adjust_stock(product_id, -quantity)
- # 2. Persist order
+ # 2. Persist order entity
self.order_repo.add(new_order)
await self.session.flush()
# 3. Buffer domain event
- # If the transaction rolls back, this event is discarded immediately!
+ # If any error occurs before commit completes, this event is discarded.
uow.register_event("order.created", {"order_id": str(new_order.id)})
```
-3. Contract-Driven Decoupling via Inversion of Control (IoC Container)
+3. Constructor-Based Dependency Injection (IoC Container)
-### The Problem
-Modular monoliths frequently suffer from circular imports when domain A (e.g., Orders) needs data from domain B (e.g., Catalog). Importing repositories or models across module boundaries creates tight database coupling.
+### Problem
+In complex applications, deep chains of FastAPI `Depends(...)` can become verbose. Furthermore, domain modules risk circular imports when cross-referencing concrete repository implementations.
-### The ZCore Solution
-Domains communicate strictly through pure Python `Protocols` (Contracts). The IoC Container auto-wires dependencies at runtime using constructor type hints with signature caching:
+### Solution
+The built-in IoC container supports constructor-based auto-wiring via type annotations, caching constructor signatures to minimize reflection overhead. It supports Singleton, Scoped, and Transient lifecycles, and automatically skips parameters with default values unless explicitly registered:
```python
-# contracts/catalog.py (No database imports!)
+# Pure Python contract (contracts/catalog.py)
from typing import Protocol, runtime_checkable
@runtime_checkable
@@ -204,11 +203,11 @@ class ProductContract(Protocol):
```
```python
-# apps/orders/services.py
+# Domain implementation (apps/orders/services.py)
from contracts.catalog import ProductContract
class OrderService(BaseService[Orders]):
- # Pure Python constructor: Auto-wired by ZCore's IoC container!
+ # Dependencies are resolved by constructor type hints
def __init__(self, repository: OrderRepository, inventory: ProductContract):
super().__init__(model=Orders, repository=repository)
self.inventory = inventory
@@ -216,35 +215,45 @@ class OrderService(BaseService[Orders]):
-4. Isolated Background Scopes (@background_task)
+4. Isolated Background Execution (@background_task)
-### The Problem
-FastAPI's built-in `BackgroundTasks` executes callables after sending the HTTP response. If you pass an active request-scoped database session to a background task, the session is closed by the time the worker executes, causing `InterfaceError: Session is closed`.
+### Problem
+FastAPI executes background tasks after completing the HTTP response. If a task receives an `AsyncSession` bound to the finished request lifecycle, the session is often closed by the time execution begins, causing connection errors.
-### The ZCore Solution
-The `@background_task` decorator creates an independent execution scope, provisions a dedicated database session, binds an isolated logging task ID, and auto-resolves annotated dependencies from the container:
+### Solution
+The `@background_task` decorator creates an independent execution scope. It provisions a dedicated database session, isolates context variables, and auto-wires type-annotated dependencies directly from the container:
```python
+import uuid
from zcore import background_task
@background_task
async def generate_invoice_task(
order_id: uuid.UUID,
- order_repo: OrderRepository, # Automatically injected with an isolated session!
+ order_repo: OrderRepository, # Injected with an independent session
) -> None:
order = await order_repo.get(id=order_id)
- # Safely executes without relying on completed HTTP request lifecycles
+ # Operates safely outside of the HTTP request lifecycle
```
-5. Zero-Side-Effect Testing Engine (ZTestClient)
+5. Composite Primary Keys & Keyset Pagination
+
+### Capabilities
+* **Composite Primary Keys:** `BaseRepository` natively handles composite models and PostgreSQL partitioned tables (e.g., `(id, operation_date)`). Operations use tuple-based lookups (`tuple_(*pks).in_(...)`), allowing PostgreSQL to perform partition pruning without full table scans.
+* **Compound Keyset Pagination:** `CursorPagination` supports non-primary-key sorting columns using compound keyset conditions `(col < val) OR (col == val AND pk < last_pk)`. It handles type coercion for `date`, `datetime`, `UUID`, and numeric types.
+* **Mutual Exclusivity:** `SearchRequest` validates against conflicting pagination modes, raising a 422 error if both offset (`page > 1`) and keyset (`cursor`) parameters are supplied simultaneously.
+
+
+
+6. Isolated Testing Harness (ZTestClient)
-### The Problem
-Integration tests that write to a database require tedious teardown scripts or slow table recreation between test cases, leading to flaky test suites.
+### Problem
+Integration tests that write directly to a database typically require slow schema teardowns or manual cleanup scripts to prevent test state leakage.
-### The ZCore Solution
-`ZTestClient` executes tests inside **transactional savepoint rollbacks**. Changes are rolled back immediately upon test completion, keeping test runs fast and deterministic:
+### Solution
+`ZTestClient` coordinates tests within transactional savepoints. Database modifications are automatically rolled back upon test completion, while container bindings and event listeners are restored from snapshots:
```python
import pytest
@@ -255,25 +264,25 @@ from main import app
async def test_order_placement_atomicity():
async with ZTestClient(
app,
- user_id="customer-uuid",
+ user_id="test-user-id",
scopes=["orders:create", "products:view"]
) as client:
response = await client.post("/checkout/orders/", json={...})
assert response.status_code == 201
- # Database is automatically rolled back to its exact pre-test state!
+ # Transaction changes are rolled back automatically upon context exit
```
---
-## π οΈ Interactive Developer Tooling (`zc` CLI)
+## π οΈ Developer Tooling (`zc` CLI)
-ZCore includes an interactive terminal user interface (TUI) powered by Questionary and Rich:
+ZCore provides an interactive command-line interface powered by Questionary and Rich:
```text
$ zc
-β‘ ZCore Framework v0.1.0-rc.2 β’ Modern Modular Monolith
+β‘ ZCore Framework v0.1.0-rc.3 β’ Modern Modular Monolith
FastAPI β’ SQLAlchemy 2.0 β’ Pydantic V2
? What framework task would you like to perform?
@@ -285,43 +294,43 @@ $ zc
πͺ exit β Exit CLI
```
-### CLI Command Summary
+### Command Reference
-| Command | Key Flags | Purpose |
+| Command | Key Options | Purpose |
| :--- | :--- | :--- |
-| `zc` | β | Open interactive terminal dashboard |
-| `zc init [name]` | `--db [sqlite\|postgres\|mysql]`, `-y` | Bootstrap project with `.env`, `.venv`, and driver dependencies |
-| `zc startapp [name]` | `--template / --no-template`, `--test`, `-y` | Generate domain module layers (models, schemas, repos, services, routers) |
-| `zc run [app]` | `--host`, `--port`, `--reload`, `--workers`, `--env-file` | Run server with cascading config hierarchy (`CLI > .env > Defaults`) |
-| `zc gensecret` | β | Generate a 64-character cryptographic `SECRET_KEY` |
-| `zc genenv` | `-o