Skip to main content

Overview

Framefox is a Python web framework built on top of FastAPI, providing a structured, modular architecture for building scalable web applications. The framework follows a layered architecture pattern with clear separation of concerns.

Core Components

The framework consists of three primary components that work together:

1. Application

The Application class is the central entry point for all interactions with Framefox, whether via web or CLI.
/home/daytona/workspace/source/framefox/application.py:14-58
class Application:
    """
    Central entry point for all interactions with the application,
    whether via web or CLI.
    """

    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if hasattr(self, "_initialized") and self._initialized:
            return

        self._container = ServiceContainer()
        self._bundle_manager = BundleManager()
        self._container.set_instance(BundleManager, self._bundle_manager)

        self._initialized = True

    def boot_web(self):
        from framefox.core.kernel import Kernel

        kernel = Kernel(self._container, self._bundle_manager)
        return kernel

    def boot_cli(self):
        registry = CommandRegistry()
        if not hasattr(self, "_cli_loaded"):
            self._bundle_manager.register_bundle_commands(registry)
            self._cli_loaded = True

        return registry
Key features:
  • Singleton Pattern: Ensures only one application instance exists
  • Dual Boot Modes: Supports both web (boot_web()) and CLI (boot_cli()) modes
  • Service Management: Initializes the ServiceContainer and BundleManager

2. Kernel

The Kernel is the core of the web application, responsible for initializing and configuring the FastAPI app.
/home/daytona/workspace/source/framefox/core/kernel.py:25-52
class Kernel:
    """
    The Kernel class is the core of the Framefox framework. It initializes and configures
    the FastAPI application, manages dependency injection, bundles, security services,
    middleware, routing, and static files.
    """

    def __init__(self, container=None, bundle_manager=None) -> None:
        if hasattr(self, "_initialized") and self._initialized:
            return
        self._container = container
        self._bundle_manager = bundle_manager

        try:
            self._settings = Settings()
            self._logger = logging.getLogger("KERNEL")
            self._app = self._create_fastapi_app()
            self._configure_app()
            self._initialized = True

        except SettingsException as e:
            StartupErrorHandler.handle_configuration_error(e)
        except Exception as e:
            self._logger.error(f"Failed to initialize Kernel: {e}")
            raise RuntimeError(f"Kernel initialization failed: {e}") from e
The Kernel’s configuration process:
/home/daytona/workspace/source/framefox/core/kernel.py:65-76
def _configure_app(self) -> None:
    """Configure the FastAPI application"""
    MiddlewareManager(self._app).setup_middlewares()
    self._setup_routing()
    self._setup_static_files()
    self._bundle_manager.boot_bundles(self._container)
    dispatcher.load_listeners()
    self._container.freeze_registry()

    self._logger.debug("Framefox application initialized successfully")
    self._logger.debug("Application configuration complete - registry frozen")

3. Bundles

Bundles are modular packages that extend Framefox functionality. They can register services, commands, and configuration.
/home/daytona/workspace/source/framefox/core/bundle/abstract_bundle.py:16-55
class AbstractBundle(ABC):
    """
    Abstract class that all bundles must implement.
    """

    @property
    @abstractmethod
    def name(self) -> str:
        """Unique name of the bundle"""
        pass

    @property
    def dependencies(self) -> List[str]:
        """List of bundles this bundle depends on"""
        return []

    def register_services(self, container: ServiceContainer) -> None:
        """Registers the bundle's services in the container"""
        pass

    def register_commands(self, registry: CommandRegistry) -> None:
        """Registers the bundle's commands in the registry"""
        pass

    def boot(self, container: ServiceContainer) -> None:
        """Executed after all bundles have been registered"""
        pass

Component Interaction

Request Lifecycle

Understanding how a request flows through Framefox:

1. Request Entry

A request enters the FastAPI application managed by the Kernel.

2. Middleware Processing

The request passes through configured middleware layers:
  • Trailing slash handler
  • Security middleware
  • Session middleware
  • Custom middleware

3. Routing

The Router matches the request to a registered route:
/home/daytona/workspace/source/framefox/core/routing/router.py:77-82
def _setup_routing(self) -> None:
    """Setup routing system and register controllers"""
    router = Router(self._app)
    self._container.set_instance(Router, router)
    router.register_controllers()

    self._logger.debug("Routing system configured")

4. Controller Resolution

The ControllerResolver lazy-loads and instantiates the controller with dependencies:
/home/daytona/workspace/source/framefox/core/controller/controller_resolver.py:70-88
def resolve_controller(self, controller_name: str) -> Any:
    # Check in mapped name cache
    if controller_name in self._controller_name_to_class:
        controller_class = self._controller_name_to_class[controller_name]
        if controller_class not in self._controller_cache.values():
            self._controller_cache[controller_name] = controller_class
        return self._create_controller_instance(controller_class)

    if controller_name in self._controller_cache:
        controller_class = self._controller_cache[controller_name]
        return self._create_controller_instance(controller_class)

    controller_class = self._load_controller_class(controller_name)
    if controller_class:
        self._controller_cache[controller_name] = controller_class
        return self._create_controller_instance(controller_class)

    searched_paths = [path for path in self._controller_paths.values()]
    raise ControllerNotFoundError(controller_name, searched_paths)

5. Dependency Injection

The Route decorator automatically injects services into controller methods:
/home/daytona/workspace/source/framefox/core/routing/decorator/route.py:27-58
@wraps(func)
async def wrapper(*args, **kwargs):
    controller_instance = args[0] if args else None

    if controller_instance and hasattr(controller_instance, "_container"):
        for param_name, param in original_sig.parameters.items():
            if param_name == "self" or param_name in kwargs:
                continue

            param_type = type_hints.get(param_name)

            if param_type and param_type != type(None):
                if (
                    self._is_fastapi_native_type(param_type)
                    or self._is_pydantic_model(param_type)
                    or self._is_primitive_type(param_type)
                    or self._is_path_parameter(param_name)
                ):
                    continue

                try:
                    service = controller_instance._container.get(param_type)
                    kwargs[param_name] = service
                except Exception as e:
                    # Handle injection failure
                    ...

    return await func(*args, **kwargs)

6. Response Generation

The controller method executes and returns a response (HTML, JSON, redirect, etc.).

7. Response Processing

The response passes back through middleware and is sent to the client.

Architecture Flow Diagram

Key Design Principles

Separation of Concerns

Each component has a specific responsibility:
  • Application: Bootstrapping and initialization
  • Kernel: Web application configuration
  • Router: Request routing
  • Controllers: Business logic
  • ServiceContainer: Dependency management

Dependency Injection

All framework components use dependency injection for loose coupling and testability.

Modular Architecture

Bundles allow extending the framework without modifying core code.

Lazy Loading

Controllers and services are loaded on-demand for optimal performance.

Next Steps

MVC Pattern

Learn how Framefox implements the MVC pattern

Dependency Injection

Master the ServiceContainer and DI system

Routing

Understand the routing system and decorators

Creating Bundles

Build custom bundles to extend Framefox

Build docs developers (and LLMs) love