- Docs
- QuadKit
- Fundamentals
- Architecture
Architecture
QuadKit’s architecture relies on explicit package boundaries. The rules vary by package family and are checked in the repository’s import-linter configuration.
1. Package boundaries are family-specific
Section titled “1. Package boundaries are family-specific”QuadKit is published as separate distributions under the shared quadkit
namespace. The intended dependency shape is not a single strict three-layer
ladder: the repository checks different boundaries for core, foundational
packages, regular extensions, AI/multimedia sub-packages, and composite
packages such as admin, CLI, and testing.
quadkit-contractsis the shared home for cross-package protocols, types, and exceptions. The boundary check prevents it from importing QuadKit implementation packages; it does have a third-partytyping-extensionsdependency.- Core is intended to stay independent of extension packages. The current import-boundary configuration records a small set of tracked exceptions.
- The foundational packages (
domain,serialization,validation, andconcurrency) can be used by extensions but are checked against imports from higher-level packages. - Regular extensions are checked for peer independence. Some concrete edges are explicitly tracked or allowed; composite packages and orchestrators have their own contracts rather than the same peer rule.
The executable rules live in the repository’s root .importlinter file. Run
them with make lint-boundaries (the namespace-aware wrapper); the bare
lint-imports command does not discover this namespace package layout. For
package dependencies and install extras, see Compatibility.
2. Shared contracts and implementation boundaries
Section titled “2. Shared contracts and implementation boundaries”A package should depend on a shared contract rather than another package’s implementation when the interface is intended to be reused. Package-local protocol modules are internal seams; the boundary rules specifically check that known local protocols are not imported across package boundaries.
This is a design direction, not a claim that every cross-package import is
currently forbidden: the configured contracts include composite-package
allowlists and explicitly tracked imports. Consult .importlinter for the
current rule set instead of assuming that all quadkit-* packages are
independent peers.
3. The application tree (one layout)
Section titled “3. The application tree (one layout)”The package graph above is not the application tree. quadkit new project currently offers one api starter and writes the canonical shape. Add installed packages with quadkit add; the layout does not change. There is no --structure flag and no models/ directory.
| Kind | Where it lives |
|---|---|
| Composition root | src/<app>/app.py — create_app(), ASGI target <app>.app:app |
| Unscoped domain types | src/<app>/domains/ |
| App providers | src/<app>/di/*_provider.py |
| Module provider | src/<app>/modules/<slug>/provider.py |
| Cross-cutting | src/<app>/shared/ |
If a skill or the CLI dump disagrees, this site wins. Full map: Project Structure.
4. The Namespace Package Layout
Section titled “4. The Namespace Package Layout”All packages publish into the shared quadkit import namespace (a PEP 420 namespace package), even though they are separate distributions:
quadkit-web/ → src/quadkit/web/ → import: from quadkit.web import ...quadkit-testing/ → src/quadkit/testing/ → import: from quadkit.testing import ...quadkit-cli/ → src/quadkit/cli/ → import: from quadkit.cli import ...So installing the quadkit-web distribution gives you the quadkit.web module. One consistent import root; many independently versioned packages underneath.
from quadkit import Application, Provider # corefrom quadkit.web import WebModule, get # quadkit-web distributionfrom quadkit.contracts.core.di import BootContainerProtocol # contracts5. How Extensions Plug In
Section titled “5. How Extensions Plug In”An extension contributes to an application in three ways, all built on the core primitives:
| Mechanism | Role | Covered in |
|---|---|---|
| Provider | Registers the extension’s services in the container and manages their lifecycle | Providers |
| Module | Bundles providers with import/export boundaries; usually exposes configure() | Modules |
| Contract | The protocol(s) the extension implements or depends on, defined in quadkit-contracts | Container Protocols |
Most extensions ship a configure() classmethod on their module so you add them in one line. The scaffold uses modules — create_app() in src/<app>/app.py:
from quadkit import Applicationfrom quadkit.web import WebModule
app = Application(name="my-app")app.add_module(WebModule.configure(discover=["my_app.controllers", "my_app.modules"]))WebModule.configure() constructs a WebProvider internally. You rarely add that provider by hand.
Boot order follows provider priority, so infrastructure providers are ready before the web layer starts serving. Full tree: Project Structure.
6. What This Buys You
Section titled “6. What This Buys You”| Property | How the package design supports it |
|---|---|
| Testability | Depend on contracts → substitute fakes from quadkit-testing with no production code change. |
| Replaceability | Swap one backend for another through config, not refactors — the package you replace is never referenced by name. |
| Incremental adoption | Start from the api scaffold and add other installed extensions as needed. |
| Clear ownership | Each package has one purpose and a well-defined surface; large teams can own packages independently. |
Next Steps
Section titled “Next Steps”- Core Concepts — providers, DI, modules, and the Result type in one place
- Project Structure — the tree generators write
- The Ecosystem — the published packages and what each one does
- Container Protocols — the type-safe contracts at the heart of the boundary
- Compatibility — extras, drivers, documented exceptions