Framework / architecture

How Lemonade Framework works.

Lemonade uses a shared configuration, service provider and dependency injection system. HTTP requests, console commands and queued jobs run in separate runtime contexts.

01
Configuration and service providersTyped settings and explicit service registration
02
DI container and registered servicesA frozen plan for consistent runtime resolution
03
Separate runtime contextsIsolated state for each individual executionRequestCommandJob
Application bootstrap

How the application is prepared

Bootstrap prepares the root container for the current entrypoint: it loads typed configuration, builds the provider plan and finalizes the required registries. It does not process a specific request yet.

01

Load configuration

ConfigLoader loads the manifest-selected definitions for HTTP or CLI into the typed configuration registry.

02

Apply runtime settings

ApplicationBootstrapper adds paths, the environment and debug mode from the current ApplicationContext.

03

Build the provider plan

Core, entrypoint, framework and application providers form one dependency-ordered plan.

04

Register services

register() declares bindings, aliases, lifetimes and tags. Runtime services are not used during this phase.

05

Freeze and boot

The container freezes its definition plan before provider boot() hooks may use the completed runtime container.

06

Finalize registries and routes

Routing.php and tagged registrars build and freeze the route table. Selecting a specific route happens later during HTTP dispatch.

Isolated scopes

Three runtime contexts. Shared infrastructure.

HTTP requests, console commands and queued jobs use services registered by the application. Each execution type has its own scope, isolating state for one request, command or job.

HTTP runtime

HTTP Request

  • Request scope
  • PSR-7 request
  • Middleware
  • Routing and controllers
CLI runtime

CLI Command

  • Command scope
  • Console commands
  • Input and output
  • Services used by the command
Queue runtime

Queue Job

  • Job scope
  • Message processing
  • Handlers
  • Per-job execution isolation
Application foundation

Shared service registrations

Each context resolves dependencies from the same container plan, while keeping scoped state inside its own scope.

DI container
Configuration
Service providers
Database services
Cache
Logging
SingletonOne instance in the root container
ScopedOne instance inside a specific scope
TransientA new instance for every resolution
Service registration

A provider declares definitions during bootstrap. Runtime services then obtain them through the DI container.

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use App\Services\InvoiceImportRegistry;
use Lemonade\Framework\Container\ContainerBuilderInterface;
use Lemonade\Framework\Core\ServiceProviderInterface;

final class AppServiceProvider implements ServiceProviderInterface
{
    public function register(ContainerBuilderInterface $builder): void
    {
        $builder->singleton(
            InvoiceImportRegistry::class,
            InvoiceImportRegistry::class,
        );
    }
}
HTTP lifecycle

From HTTP request to response

A normal request passes through the global PSR-15 pipeline, routing and the selected controller. The resulting response travels back through the middleware pipeline before the kernel emits it.

01

Request

The kernel passes the scope-bound PSR-7 ServerRequest into the global middleware pipeline.

02

Middleware

Global PSR-15 middleware wrap dispatch and may return a response before routing.

03

Router

The router matches the HTTP method and path, producing an immutable RouteMatch.

04

Controller

Route middleware wrap the selected handler; the resolver builds the controller and supplies action arguments.

05

Response

The action result is normalized to a PSR-7 response and ResponseEmitter sends its status, headers and body.

The response travels back through the active middleware pipeline
Architecture principles

Clear boundaries between framework and application

Explicit routing

HTTP methods and paths map to concrete controller actions.

Separated service lifetimes

The container distinguishes singleton, transient and scoped services. Request, Command and Job use their own isolated scopes.

Application ownership

The framework provides infrastructure and integration contracts. The application owns domain logic, content structure and concrete integrations.