Kamis, 01 Mei 2025

Action-Domain-Responder VS. MVC: Laravel Implementation Example

| Kamis, 01 Mei 2025

Basically, Action-Domain-Responder (ADR) is simply an improved version of the classic Model-View-Controller (MVC) pattern, nothing more.

Here are the main advantages of ADR compared to MVC:

1. Better Domain Separation and Application Structure

  • ADR was originally designed with Domain-Driven Design (DDD) principles in mind, which allows better structuring of the application by domains rather than by component types (controllers, models, views). As a result, the code structure becomes more logical and focused on business goals rather than technical layers.

  • In MVC, you often end up with many folders full of controllers and mixed responsibilities, whereas ADR suggests grouping code by meaning and purpose, which makes maintenance and scaling easier.

2. Clear Separation of Responsibilities

In ADR, each component has a strictly defined role:

  • Action – handles business logic and interacts with the domain.
  • Domain – the business domain model.
  • Responder – builds the HTTP response (including headers and content).

In MVC, controllers often get bloated with business logic and responsibility for generating the response, which complicates maintenance and testing. In ADR, response generation is completely separated from business logic, improving readability and simplifying testing.

3. Closer Alignment with Web Architecture

  • ADR better reflects the real flow of web applications: a request goes to an action, the action interacts with the domain, then the response is formed. In MVC, the concept of a “view” is often mistakenly equated with a template, while in ADR the responder is responsible for the entire HTTP response.

  • ADR encourages the use of middleware and request interceptors, allowing authorization and other checks to be moved out of actions, further separating responsibilities.

4. Improved Maintainability and Scalability

Although ADR increases the number of classes (each action and responder is a separate class), this leads to a flatter and clearer hierarchy, making it easier to maintain and extend the application in the long term.

Example of ADR in Laravel

Action class:

<?php declare(strict_types=1);

namespace App\Account\User\Presentation\Action;

use App\Shared\Presentation\Controller;
use App\Shared\Domain\Bus\QueryBusInterface;
use App\Account\User\Application\Query\GetUsersPaginationQuery;
use Spatie\RouteAttributes\Attributes\Prefix;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Route;
use Spatie\RouteAttributes\Attributes\WhereUuid;
use App\Account\User\Presentation\Responder\IndexResponder;
use App\Shared\Presentation\Response\ResourceResponse;

#[Prefix(prefix: 'v1')]
#[Middleware(middleware: 'auth:api')]
#[WhereNumber(param: 'perPage')]
final class IndexAction extends Controller
{
    private QueryBusInterface $queryBus;

    public function __construct(QueryBusInterface $queryBus)
    {
        $this->queryBus = $queryBus;
    }

    #[Route(methods: ['GET'], uri: '/users/{perPage?}')]
    public function __invoke(int $perPage = 11): ResourceResponse
    {
        $query = new GetUsersPaginationQuery(perPage: $perPage);

        return new IndexResponder()->respond(
            data: $this->queryBus->ask(query: $query)
        );
    }
}

Responder class:

<?php declare(strict_types=1);

namespace App\Account\User\Presentation\Responder;

use App\Account\User\Presentation\AccountResource;
use App\Shared\Presentation\Response\ResourceResponse;
use Illuminate\Http\Response;

final readonly class IndexResponder
{
    public function respond(mixed $data): ResourceResponse
    {
        if (!blank(value: $data)) {
            return new ResourceResponse(
                data: AccountResource::collection(resource: $data),
                status: Response::HTTP_OK
            );
        }

        return new ResourceResponse(
            data: ['message' => __('No Users Found.')],
            status: Response::HTTP_NOT_FOUND
        );
    }
}

Structurally, it might look like this:

DDD Structura In Laravel

Or like this:

Base Structura In Laravel

In conclusion

ADR is an evolution of MVC, adapted specifically for the web, providing better separation of concerns, cleaner architecture, and easier work with modern web applications.

Thus, Action-Domain-Responder is better than MVC because it:

  • Offers a more logical division of the application by domains rather than technical layers.
  • Clearly separates business logic handling from HTTP response formation.
  • Better matches the real flow of web interactions.
  • Encourages the use of middleware for code separation.
  • Provides simpler maintenance and scalability thanks to a clear class structure.

P. S. Subscribe to my Telegram channel - @initialstack where I share insights on structuring and architecting PHP applications with the Laravel framework.


Related Posts

Tidak ada komentar:

Posting Komentar