Cullinan Framework Architecture¶
Version: 0.93a13 Last Updated: 2026-06-01
Status: UpdatedThis page explains framework structure, not the default startup tutorial.
Start from Application Build for the recommended bootstrap path, and use Internals & Extensions when you intentionally need deeper runtime details.
Overview¶
Cullinan is an engine-neutral application framework whose current runtime is organized around three consolidated pillars:
- Unified container facade —
cullinan.coreis the public IoC/DI entrypoint. - Transport-agnostic Web Runtime —
cullinan.web.gatewayownsWebRequest,WebResponse, routing, dispatch, middleware, and exception handling. - Decorator-first runtime assembly — application code starts from business decorators, while runtime ownership and hot-pluggable stability are layered in when needed.
Architecture layers¶
Application code
├── @service / @controller / @component
├── Controller methods with get_api/post_api/...
└── Business services and middleware
Framework facade
├── cullinan -> @application, configure/run/get_asgi_app
├── cullinan.application -> Application, @module
├── cullinan.web -> controller decorators, WebRequest/WebResponse, params, middleware
├── cullinan.core -> ApplicationContext, scopes, lifecycle, request context
├── cullinan.testing -> testing helpers and verification entrypoints
├── cullinan.runtime -> discovery, scanning, runtime assembly
└── cullinan.transport -> WebAdapter, TornadoAdapter, ASGIAdapter
Runtime execution
├── Decorator declarations -> import-executed discovery -> runtime assembly
├── ApplicationContext.refresh()
├── Gateway pipeline + dispatcher
├── Adapter-specific request/response translation
└── ApplicationContext.shutdown()
Semantic package surface¶
Cullinan's recommended package surface now follows a clearer framework-semantic split:
cullinan— default startup surface (configure,run,get_asgi_app)cullinan.application— advanced application semantics such as application definition and runtime boundarycullinan.web— business-facing Web development surfacecullinan.core— IoC/DI, lifecycle, request context, semantic diagnosticscullinan.testing— test-facing supportcullinan.runtime— discovery, scanning, and runtime assembly internalscullinan.transport— server adapter boundarycullinan.support— constrained support utilities, not a default first-read surface
This split keeps the default path business-first while still giving maintainers and advanced users explicit lower-level layers. Historical root-level wrappers such as cullinan.app or cullinan.public_api are no longer part of the maintained structure.
Core container model¶
cullinan.core exposes the public container API. ApplicationContext is the single runtime entrypoint for registration, resolution, refresh, and shutdown.
Main responsibilities¶
- Register dependency definitions
- Resolve singleton / prototype / request-scoped instances
- Drive lifecycle hooks during
refresh()andshutdown() - Hold the active application context used by framework integrations
Public flow¶
from cullinan.core import ApplicationContext, set_application_context
ctx = ApplicationContext()
set_application_context(ctx)
# new application code starts from decorators and runtime assembly;
# explicit Definition registration is reserved for low-level integration
ctx.refresh()
...
ctx.shutdown()
Legacy cullinan.core.container.* modules remain as thin forwards. They are no longer separate stateful entrypoints.
Dependency injection and lifecycle¶
The decorator layer (@service, @controller, Inject, InjectByName) feeds the unified container model.
Recommended usage¶
- Use
@serviceand@controllerfor normal application code - Use
Inject()for type-safe field injection - Use
InjectByName()when name-based resolution is clearer or avoids circular imports - Use
ApplicationContextdirectly for custom factories or third-party integration
Lifecycle hooks¶
All managed components participate in the same lifecycle contract:
on_post_construct()on_startup()on_shutdown()on_pre_destroy()
Async hook variants with _async are supported. Ordering can be influenced with get_phase().
Web Runtime¶
The current web stack is centered on cullinan.web.gateway.web_core, but the recommended business-facing entry now goes through cullinan.web.
Public runtime objects¶
WebRequest— normalized request objectWebResponse— mutable response builder that can be frozen before writingRouter— route registration and matchingDispatcher— request dispatch and return-value handlingMiddlewarePipeline— onion-style middleware compositionExceptionHandler— exception-to-response conversionWebRuntime— active runtime state and runtime switching
Adapter boundary¶
Server integration lives behind cullinan.transport and its underlying cullinan.transport.adapter implementation:
WebAdapter— common adapter contractTornadoAdapter— Tornado integrationASGIAdapter— ASGI integration
This split keeps request processing logic independent from any single server implementation.
Request flow¶
- Business code declares services, controllers, and handlers with decorators.
- Runtime assembly imports owned Python modules and rebuilds registrations from decorator metadata.
ApplicationContext.refresh()resolves eager components and runs startup hooks.- The gateway pipeline receives a transport-normalized
WebRequest. Dispatchermatches a route, resolves parameters, invokes the handler, and produces aWebResponse.- A concrete adapter writes the response back to Tornado or ASGI.
Cullinan's default developer story is now framework semantics first, runtime backend second: application code targets Cullinan's request/response, controller, parameter, middleware, and lifecycle model, while Tornado and ASGI sit behind the adapter boundary as execution backends.
7. Shutdown calls ctx.shutdown() and drains managed lifecycle hooks.
Testing strategy¶
The repository uses pytest as the single supported test runner.
- Formal repository command:
.venv\Scripts\python -m pytest - Shared test bootstrap lives in
tests/conftest.py - Test layout is topic-based:
tests/core,tests/di,tests/web,tests/integration,tests/regression,tests/compat
See Testing & Verification for the current workflow.