Skip to main content

12. Herramientas de desarrollo

12.1. Comandos Artesano

El framework implementa un sistema de comandos CLI denominado "Artesano", similar a Artisan en Laravel, que facilita la generación de código y automatiza tareas repetitivas para los desarrolladores. Este sistema está construido sobre el componente Console de Symfony.

Arquitectura

El ejecutable principal artesano ubicado en la raíz del proyecto inicializa una aplicación de consola Symfony:

#!/usr/bin/env php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Console\Application;
use App\Commands\CreateDomainCommand;
use App\Commands\CreateLegacyInfraestructureCommand;

$app = new Application();

try {
$createDomainCommand = new CreateDomainCommand();
$createDomainCommand->setAliases(['cd', 'create-domain']);

$createLegacyCommand = new CreateLegacyInfraestructureCommand();
$createLegacyCommand->setAliases(['cli', 'create-legacy']);

$app->add($createDomainCommand);
$app->add($createLegacyCommand);

} catch (\Exception $e) {
echo "Error al registrar comandos: " . $e->getMessage() . "\n";
echo $e->getTraceAsString() . "\n";
}

$app->run();

Los comandos se implementan como clases PHP que extienden de Symfony\Component\Console\Command\Command y se registran en la aplicación principal, permitiendo ejecutarlos mediante el CLI.

12.2. Generación de código

El framework incluye dos comandos principales para la generación de código que aceleran el desarrollo:

CreateDomainCommand

Este comando genera automáticamente la estructura completa de un nuevo dominio de negocio, creando todos los archivos necesarios según la arquitectura hexagonal implementada en el framework:

class CreateDomainCommand extends Command
{
protected function configure()
{
$this->setName('app:create-domain')
->setDescription('Crea un nuevo Domain con su estructura basica')
->addArgument('domain', InputArgument::REQUIRED, 'Nombre del domain')
->addArgument('infrastructure', InputArgument::REQUIRED, 'Nombre de la infraestructura principal')
->addArgument('module', InputArgument::OPTIONAL, 'Nombre del modulo');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
// Lógica para generar la estructura del dominio
// ...
}
}

El comando crea:

  • Controlador del dominio
  • Servicio del dominio
  • Rutas para los endpoints REST
  • DTOs para solicitudes y respuestas
  • Estructura de directorios adecuada

Ejemplo de uso:

php artesano app:create-domain Products p-maes_maesarti Catalog

Este comando generaría un nuevo dominio "Products" que utiliza la infraestructura "maesarti" del módulo POS ("p-maes") dentro del módulo "Catalog".

CreateLegacyInfraestructureCommand

Este comando genera la estructura de infraestructura para integrar con sistemas legacy, particularmente con el sistema Quartup:

class CreateLegacyInfraestructureCommand extends Command
{
protected static $defaultName = 'app:create-legacy-infrastructure';
protected $basePath = 'src/Infrastructures/Legacy/';

protected function configure()
{
$this
->setName('app:create-legacy-infrastructure')
->setDescription('Create a new legacy infrastructure')
->addArgument('module', InputArgument::REQUIRED, 'Nombre del modulo P Quartup')
->addArgument('resource', InputArgument::REQUIRED, 'Nombre amigable de la tabla')
->addArgument('table', InputArgument::REQUIRED, 'Nombre de la tabla legacy')
->setHelp('Este comando crea una nueva infraestructura legacy');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
// Lógica para generar la infraestructura legacy
// ...
}
}

Crea:

  • Modelo para la tabla legacy
  • Repositorio correspondiente
  • Configuración de mapeo entre el sistema legacy y la API

Ejemplo de uso:

php artesano app:create-legacy-infrastructure PPos PosartticVArticulo posarttic

Este comando crearía la infraestructura para la tabla "posarttic" del módulo "PPos" de Quartup, con un nombre amigable de "PosartticVArticulo".

12.3. Generación de plantillas

Los comandos de generación utilizan plantillas incorporadas para crear archivos con una estructura consistente. Por ejemplo, para la creación de un servicio de dominio:

private function getServiceTemplate($module, $domain, $infrastructure, $infrastructureNamespace, $infrastructureCamelFirstLower)
{
$namespace = $module
? "ApiLayer\\Modules\\{$module}\\Domains\\{$domain}"
: "ApiLayer\\Domains\\{$domain}";

return "<?php

namespace {$namespace};

use App\Base\DomainService;
use App\Core\RequestFactory as Request;
use {$infrastructureNamespace}\\{$infrastructure}Repository;
use {$namespace}\DTOs\\Res{$infrastructure}DTO;
use {$namespace}\DTOs\Req{$infrastructure}DTO;

class {$domain}Service extends DomainService
{
protected \${$infrastructureCamelFirstLower}Repository;
protected \$res{$infrastructure}DTO;
protected \$req{$infrastructure}DTO;

public function __construct(
{$infrastructure}Repository \${$infrastructureCamelFirstLower}Repository,
Res{$infrastructure}DTO \$res{$infrastructure}DTO,
Req{$infrastructure}DTO \$req{$infrastructure}DTO
) {
\$this->{$infrastructureCamelFirstLower}Repository = \${$infrastructureCamelFirstLower}Repository;
\$this->res{$infrastructure}DTO = \$res{$infrastructure}DTO;
\$this->req{$infrastructure}DTO = \$req{$infrastructure}DTO;
\$this->{$infrastructureCamelFirstLower}Repository->selectable(Res{$infrastructure}DTO::\$selectables);
}

// Métodos CRUD y de servicio
// ...
}";
}

Estos métodos de generación de plantillas son extensivos y cubren todos los componentes necesarios para la implementación de dominios completos.

12.4. Debugging y pruebas

Herramienta de debugging

El framework incluye una función de debugging global ddd() (dump, die, debug) para facilitar la inspección de variables durante el desarrollo:

if (!function_exists('ddd')) {
function ddd(...$a)
{
QuApp::markAsNormalTermination();
dd(...$a);
}
}

Esta función marca la terminación como normal en la aplicación antes de volcar los datos, lo que permite evitar la activación de los manejadores de error cuando se utiliza deliberadamente.

Configuración de entorno

El framework detecta automáticamente el entorno de ejecución (desarrollo o producción):

static public function setEnvironment($env = null)
{
self::$environment = 'PROD';

if (isset(self::$QuupIni['id_domain'])) {
if (self::$QuupIni['id_domain'] == 3) {
self::$environment = 'DEV';
}
}

if ($env) {
if ('PROD' === strtoupper($env)) {
self::$environment = 'PROD';
return;
}
if ('DEV' === strtoupper($env)) {
self::$environment = 'DEV';
return;
}
}
return;
}

El entorno determina el comportamiento del sistema en cuanto a:

  • Nivel de detalle en los mensajes de error
  • Mostrado de datos sensibles
  • Cantidad de información de debugging expuesta

Trazas de errores

Para facilitar la depuración, el framework proporciona un sistema de trazas de errores:

try {
/**
* procesamos la peticiónn y obtenemos la respuesta
*/
$response = $kernel->handle($request);
QuApp::setSuccessResponse($response);
} catch (\Throwable $th) {
if (ENVIRONMENT === "DEV") {
QuApp::setTraceErrors(true);
}

QuApp::setErrorResponse($th);
}

En el entorno de desarrollo, las trazas completas de error son capturadas y formateadas para ayudar a identificar la fuente del problema.

Conversión de errores a excepciones

El framework implementa un mecanismo para convertir los errores PHP en excepciones, lo que facilita el manejo uniforme de errores:

class QuErrorsToException
{
protected $errorLevelsToConvert;

public function __construct($errorLevelsToConvert = E_ALL)
{
$this->errorLevelsToConvert = $errorLevelsToConvert;
}

public function register()
{
set_error_handler([$this, 'handleError']);
}

public function handleError($severity, $message, $file, $line)
{
if (!($severity & $this->errorLevelsToConvert)) {
return false;
}

throw new ErrorException($message, 0, $severity, $file, $line);
}
}

Esta clase permite configurar qué tipos de errores deben convertirse en excepciones, y se inicializa de manera diferente según el entorno:

if (ENVIRONMENT == 'DEV') {
$converter = new QuErrorsToException(E_ALL); // Todo menos notice
} else {
$converter = new QuErrorsToException(E_ALL & ~(E_NOTICE | E_WARNING)); // todo menos warning y notice
}
$converter->register();

Estructura de herramientas de desarrollo

Con estas herramientas, el framework ofrece un entorno de desarrollo más productivo donde los desarrolladores pueden:

  1. Generar rápidamente nuevos componentes con estructura consistente
  2. Integrar fácilmente con sistemas legacy
  3. Depurar eficientemente las aplicaciones
  4. Manejar errores de forma consistente y rastreable

Esta serie de herramientas reduce significativamente el tiempo de desarrollo y mantiene la coherencia en la base de código, asegurando que todos los componentes sigan los patrones de diseño establecidos.