---
title: "MVC pattern and request flow in Extbase"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extbase-start-principles@main"
source: "ExtensionArchitecture/Extbase/Concepts/Mvc.rst"
rendered: "2026-09-24T12:36:55+00:00"
---

# MVC pattern and request flow in Extbase {#extbase-start-principles}

Extbase structures extensions around the
[Model-View-Controller (MVC)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)
pattern. MVC separates three concerns that have a tendency to become entangled
in less structured code:

-   **Model** — what your data looks like and how it is persisted
-   **View** — how the data is presented to the user
-   **Controller** — what happens when an HTTP request arrives and which data
    the response should contain

To make this concrete: a visitor clicks "Read more" on a conference listing. The
browser sends an HTTP GET request to a URL like
`/conferences?tx_myextension_conferencelist[action]=show&tx_myextension_conferencelist[conference]=42`.
The **controller** receives that request, asks the **model** layer (the
repository) for the `Conference` with UID 42, hands the object to the
**view**, and the view renders it as HTML. Each layer does one job and one
job only.

Keeping these three things separate means you can change how data is stored
without touching the templates, and change the output format without touching
the business logic. It also means that any TYPO3 developer who knows Extbase
can immediately orient themselves in an unfamiliar extension.

**On this page**

-   [The three Extbase layers](https://docs.typo3.org/permalink/t3coreapi:the-three-extbase-layers@main)
-   [How a request flows through Extbase](https://docs.typo3.org/permalink/t3coreapi:how-a-request-flows-through-extbase@main)
-   [How Extbase MVC differs from other frameworks](https://docs.typo3.org/permalink/t3coreapi:how-extbase-mvc-differs-from-other-frameworks@main)
-   [Controller actions in Extbase](https://docs.typo3.org/permalink/t3coreapi:controller-actions-in-extbase@main)
-   [Extbase action responses](https://docs.typo3.org/permalink/t3coreapi:extbase-action-responses@main)

## The three Extbase layers {#extbase-concepts-mvc-layers}

-   ****Model****

    Domain objects — PHP classes that represent the data your extension works
    with. An event, a product, a blog post. Models extend
    `\TYPO3\CMS\Extbase\DomainObject\AbstractEntity` and are stored in
    `Classes/Domain/Model/`. Their properties map to database columns.
    Models know nothing about HTTP, templates, or how they are displayed.

    Repositories sit alongside models and are the only entry point to the
    database. A controller should never query the database directly — it asks a
    repository for objects.

-   ****View****

    Fluid templates reside in `Resources/Private/Templates/`. The view receives
    variables from the controller and renders them as HTML (or JSON, or any
    other format). The view knows nothing about where data came from or what
    triggered the request.

-   ****Controller****

    The coordinator. It receives a request, asks repositories for the data it
    needs, applies business logic as needed and hands that data to the view,
    and returns a response. Controllers live in `Classes/Controller/` and extend
    `\TYPO3\CMS\Extbase\Mvc\Controller\ActionController`.

The strict separation is intentional. When something goes wrong, you know
exactly which layer to look at.

## How a request flows through Extbase {#extbase-concepts-mvc-request-flow}

When TYPO3 renders a page containing an Extbase plugin, the following sequence
happens:

1.  **TYPO3 hands off to Extbase**

    The plugin content element is rendered by the TYPO3 content object renderer.
    Control passes to `\TYPO3\CMS\Extbase\Core\Bootstrap`, which reads the
    plugin configuration to determine which extension and plugin is involved.
1.  **An Extbase request object is built**

    TYPO3 receives the browser request as a
    [PSR-7](https://www.php-fig.org/psr/psr-7/) server request object.
    PSR-7 is a
    [PHP-FIG](https://www.php-fig.org/) standard that defines a common
    interface for HTTP messages — requests and responses — so that frameworks
    and libraries can interoperate without depending on each other's
    implementations. Extbase wraps that PSR-7 request in its own
    `\TYPO3\CMS\Extbase\Mvc\Request` object, which carries the controller
    name, action name, and any arguments extracted from the URL or POST data —
    for example, the UID of a record to display.
1.  **The dispatcher resolves the controller**

    `\TYPO3\CMS\Extbase\Mvc\Dispatcher` looks up which controller class
    corresponds to the requested controller name and instantiates it via the
    DI container — the component responsible for
    constructing objects and automatically providing their dependencies, so your
    controller receives everything it needs without wiring it up manually. See
    [Dependency injection](https://docs.typo3.org/permalink/t3coreapi:dependency-injection@main) for details.
1.  **The controller action runs**

    The dispatcher calls `processRequest()` on the controller. The
    controller resolves the action method name (for example `listAction` or
    `showAction`), maps incoming arguments to typed PHP parameters via
    property mapping, runs validation, and then calls the action method.
1.  **The action builds the response**

    Inside the action, the controller assigns variables to the view and returns
    a `ResponseInterface`. For a standard HTML response this means calling
    `$this->htmlResponse()`, which renders the matching Fluid template and
    wraps the output in a PSR-7 response.
1.  **TYPO3 renders the response into the page**

    The rendered HTML is returned to the content object renderer and inserted
    into the page at the position of the plugin content element.

> [!NOTE]
> If an action calls `$this->redirect()` or returns a
> `\TYPO3\CMS\Extbase\Http\ForwardResponse`, the dispatcher loops and
> processes the new target action before returning a final response. This is
> how multi-step flows (for example: form → validate → confirm) work within a
> single page request.

## How Extbase MVC differs from other frameworks {#extbase-concepts-mvc-typo3-difference}

Developers coming from Symfony or Laravel may expect the framework to dispatch
the entire URL to a controller. Extbase does not work this way.

In TYPO3, the **page router** handles the URL first and resolves it to a page
in the page tree. Only after a page is determined does TYPO3 render its content
elements — and only at that point, when a content element of the plugin's type
is encountered, does Extbase take over.

This has two practical consequences:

-   **Multiple plugins can live on one page.** Each plugin content element is
    rendered independently. Two or more Extbase plugins, which may belong to different extensions,
    can comfortably live on the same page and each runs their own request dispatch cycle.
    URL arguments are namespaced per plugin (for example `tx_myextension_conferencelist[action]=show`)
    to avoid collisions.
-   **Extbase does not control the page URL.** Clean URLs require a route
    enhancer configured in the site's `config.yaml`. Without one, plugin
    arguments appear as query parameters. See
    [Routing for Extbase plugins](https://docs.typo3.org/permalink/t3coreapi:extbase-routing@main) for the full
    configuration.

If you are used to a framework where every route maps to exactly one
controller, it helps to think of an Extbase plugin as a self-contained
sub-application embedded inside a TYPO3 page — the page router gets you to the
page, then Extbase dispatches within that boundary.

## Controller actions in Extbase {#extbase-concepts-mvc-actions}

Each public method in a controller whose name ends in `Action` is a
potential action. The action name in the URL is the method name without the
`Action` suffix, lowercased: `listAction()` is addressed as
`action=list`.

Actions can declare typed parameters. Extbase's property mapping resolves them
automatically from the request:

**EXT:my_extension/Classes/Controller/ConferenceController.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\Conference;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
  public function showAction(Conference $event): ResponseInterface
  {
    $this->view->assign('event', $event);
    return $this->htmlResponse();
  }
}

```

When the URL contains `event=42`, Extbase loads the `Conference` object
with UID 42 from the repository and passes it directly to the action. You never
write a repository lookup for this — the framework handles it.

Primitive types work the same way: a parameter typed `int` receives an
integer, `string` a string. Validation is executed before the action is called;
if it fails, `errorAction()` handles the error rather than your action
running with invalid data.

## Extbase action responses {#extbase-concepts-mvc-responses}

Actions must return a `\Psr\Http\Message\ResponseInterface`. The two most
common helpers on `\TYPO3\CMS\Extbase\Mvc\Controller\ActionController` are:

-   `$this->htmlResponse()` — renders the Fluid template matching the
    current action and returns a HTML response with status code 200 (OK)
-   `$this->jsonResponse()` — returns a JSON response, also with status code 200 (OK);
    use with `\TYPO3\CMS\Extbase\Mvc\View\JsonView` to control which properties
    are serialized

For redirects and forwards:

-   `$this->redirect('list')` — creates a redirect via request with 303 status code (SEE OTHER)
    to another action
-   `return new \TYPO3\CMS\Extbase\Http\ForwardResponse('list')` — passes
    control to another action within the same request, without a redirect

> [!NOTE]
> **See also**
>
> [ActionController: actions, arguments and responses](https://docs.typo3.org/permalink/t3coreapi:extbase-controller-action@main)
> — the full controller reference, including argument handling, error actions,
> and backend controllers.
>
> [Extbase domain model](https://docs.typo3.org/permalink/t3coreapi:extbase-domain-model@main) — how models and
> repositories work.
>
> [View layer in Extbase](https://docs.typo3.org/permalink/t3coreapi:extbase-view-overview@main) — Fluid templates and
> response types.
