Software design principles
The following principles are considered best practices and are good to know when you develop extensions for TYPO3.
- Object-Oriented Programming (OOP) organizes software design around data, or objects, rather than the functions and logic of procedural programming.
- Domain-Driven Design (DDD) is a collection of principles and patterns that help developers design elegant code around clearly defined conceptual models.
- Model-View-Controller (MVC) is a programming paradigm that leads to clear isolation of data, presentation layer, and business logic.
- Test-Driven Development (TDD) is a basic technique for generating code that is stable, resilient to errors, and legible — and therefore maintainable.
- Dependency Injection is a software design pattern that adds flexibility by removing the need for hard-coded dependencies.
- Separation of Concerns and Single-responsibility principle (SRP) are computer programming principles that help developers create modular, coherent, and maintainable software.
We also recommend to study common software design patterns.
Data Transfer Objects (DTO) as a software design concept
A very common pattern in Extbase extensions is a DTO.
A DTO is an instance of a basic class that usually only has a constructor,
getters and setters. It is not meant to be an extension of an Extbase Abstract.
This DTO serves as pure data storage. You can use it to receive and retrieve
data in a <f: Fluid
CRUD
("Create Read Update Delete") setup.
Later on, the DTO can be accessed and converted into a "proper" Extbase domain model entity: The DTO getters retrieve the data, and the Extbase domain model entity's setters receive that data:
<?php
declare(strict_types=1);
// The actual "plain" DTO, just setters and getters
class PersonDTO
{
protected string $first_name;
protected string $last_name;
public function getFirstName(): string
{
return $this->first_name;
}
public function setFirstName(string $first_name): void
{
$this->first_name = $first_name;
}
public function getLastName(): string
{
return $this->last_name;
}
public function setLastName(string $last_name): void
{
$this->last_name = $last_name;
}
}
// The Extbase domain model entity.
// Note that the getters and setters can easily be mapped
// to the DTO due to their same names!
class Person extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
protected string $first_name;
protected string $last_name;
public function getFirstName(): string
{
return $this->first_name;
}
public function setFirstName(string $first_name): void
{
$this->first_name = $first_name;
}
public function getLastName(): string
{
return $this->last_name;
}
public function setLastName(string $last_name): void
{
$this->last_name = $last_name;
}
}
// An Extbase controller utilizing DTO-to-entity transfer
class DtoController extends TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
public function __construct(protected MyVendor\MyExtension\Domain\Repository\PersonRepository $personRepository) {}
public function createAction(): Psr\Http\Message\ResponseInterface
{
// Set up a DTO to be filled with input data.
// The Fluid template would use <f:form> and its helpers.
$this->view->assign('personDTO', new PersonDTO());
}
public function saveAction(PersonDTO $personDTO): Psr\Http\Message\ResponseInterface
{
// Transfer all data to a proper Extbase entity.
// Create an empty entity first:
$person = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(Person::class);
// Use setters/getters for propagation
$person->setFirstName($personDTO->getFirstName());
$person->setLastName($personDTO->getLastName());
// Persist the extbase entity
$this->personRepository->add($person);
// The "old" DTO needs to further processing.
}
}
DTOs are helpful because:
- They allow to decouple pure data from processed/validated data in entities.
- They allow to structure data into distinct data models.
- They allow to use good type hinting and using setters/getters instead of using untyped and unstructured PHP arrays for data storage.
- They can be used to hide implementation details of your actual entities by transferring data like filter settings that internally gets applied to actual data models.