2. Arquitectura del sistema
2.1. Patrones de diseño implementados
La arquitectura del sistema está construida sobre patrones de diseño bien establecidos que contribuyen a su modularidad, extensibilidad y mantenibilidad. Estos patrones se han implementado de forma consistente a través de todo el código base.
2.1.1. Patrón Repositorio
El patrón Repositorio actúa como una capa de abstracción entre la lógica de negocio y la fuente de datos subyacente, aislando el código de dominio de los detalles específicos de persistencia.
Implementación:
- Se define a través de la interfaz
RepositoryInterfacey la clase baseRepository - Cada entidad de dominio tiene su propio repositorio (ej.
ProductsRepository,MaesartiProductosRepository) - Los repositorios se conectan a la capa de modelos que interactúan directamente con la infraestructura legacy
Ejemplo:
class PosartticVArticuloRepository extends Repository
{
public function __construct(PosartticVArticuloModel $model)
{
parent::__construct($model);
}
public function getByCodOrAcceso($codOrAcceso)
{
$articulo = $this->selectOne(['cod_articulo' => $codOrAcceso]);
if ($articulo) {
return $articulo;
}
return $this->selectOne(['acceso' => $codOrAcceso]);
}
}
Beneficios:
- Centraliza la lógica de acceso a datos
- Facilita el testing mediante mocks o stubs
- Permite migrar o cambiar la fuente de datos sin afectar la lógica de negocio
- Simplifica la gestión de transacciones a través de métodos estándar
2.1.2. Patrón Servicio
El patrón Servicio encapsula la lógica de negocio de un dominio específico, coordinando las operaciones entre repositorios y otros servicios.
Implementación:
- Se define a través de la interfaz
ServiceInterfacey la clase baseDomainService - Cada dominio tiene su servicio correspondiente (ej.
ProductsService,CartsService) - Los servicios contienen la lógica de negocio y utilizan repositorios para el acceso a datos
Ejemplo:
class ProductsService extends DomainService
{
protected $MaesartiProductosRepository;
protected $CreateMaesartiProductosDTO;
protected $MaesartiProductosDTO;
public function __construct(
MaesartiProductosRepository $MaesartiProductosRepository,
InterfaceReqDto $CreateMaesartiProductosDTO,
InterfaceResDto $MaesartiProductosDTO
) {
$this->MaesartiProductosRepository = $MaesartiProductosRepository;
$this->CreateMaesartiProductosDTO = $CreateMaesartiProductosDTO;
$this->MaesartiProductosRepository->selectable(MaesartiProductosDTO::$selectables);
$this->MaesartiProductosDTO = $MaesartiProductosDTO;
}
public function index(Request $request)
{
$this->CreateMaesartiProductosDTO->mapFilters($request);
$resources = $this->MaesartiProductosRepository->collection($request->all());
$resources['collection'] = $this->mapToDTO($resources['collection'], $this->MaesartiProductosDTO);
return $resources;
}
// Otros métodos CRUD y de negocio...
}
Beneficios:
- Separa la lógica de negocio de controladores y modelos
- Permite la reutilización de lógica a través de diferentes puntos de entrada
- Facilita la implementación de reglas de negocio complejas
- Mejora la testabilidad del sistema
2.1.3. Patrón Controlador
El patrón Controlador gestiona las solicitudes HTTP, delegando el procesamiento a los servicios apropiados y formateando las respuestas.
Implementación:
- Se define a través de la interfaz
ControllerInterfacey clases base comoControlleryDomainController - Cada dominio tiene su controlador correspondiente (ej.
ProductsController,CartsController) - Los controladores reciben solicitudes HTTP, las validan, las delegan a servicios y formatean las respuestas
Ejemplo:
class CartsController extends Controller
{
protected $CartsService;
public function __construct(CartsService $CartsService)
{
$this->CartsService = $CartsService;
}
public function store(Request $request)
{
ValidaRequest::make($request)
->validateHeaders([
'x-tienda' => 'required|numeric',
])
->validateBody([
'user_id' => 'required|numeric',
])
->action('store')
->handle();
$xTienda = $request->headers->get('x-tienda');
$request->request->add(['x_tienda' => $xTienda]);
return response()->json($this->CartsService->store($request->all()), 201);
}
// Otros métodos para gestionar diferentes endpoints...
}
Beneficios:
- Establece un punto único para el manejo de solicitudes HTTP
- Separa la lógica de presentación de la lógica de negocio
- Facilita la implementación de validaciones de entrada
- Estandariza el formato de las respuestas
2.1.4. Patrón DTO (Data Transfer Object)
El patrón DTO se utiliza para encapsular datos que se transfieren entre capas del sistema, normalizando estructuras y validando campos.
Implementación:
- Se definen a través de las interfaces
InterfaceReqDtoyInterfaceResDto - Implementados en clases base como
ReqDtoyResDto - Dos tipos principales: DTOs de entrada (Request) y DTOs de salida (Response)
Ejemplo de DTO de respuesta:
class PosartticVArticuloDTO extends ResDto implements InterfaceResDto
{
public static $selectables = [
"id", "postie_id", "id_maesarti", "cod_articulo", "acceso", "nombre", "posartcla_id",
// Otros campos...
];
protected $id;
protected $postie_id;
protected $nombre;
// Otras propiedades...
public function handle(array $data): InterfaceResDto
{
$this->id = $data['id'];
$this->nombre = $data['nombre'];
// Asignación de otras propiedades...
return $this;
}
public function toArray(): array
{
return [
'id' => (string) $this->id,
'name' => (string) trim($this->nombre),
'price' => (float) $this->posarttic_prec_mone,
// Transformación de otros campos...
];
}
}
Beneficios:
- Proporciona un contrato claro para la transferencia de datos
- Facilita la transformación entre formatos internos y externos
- Permite la validación centralizada de datos
- Evita la exposición directa de entidades de dominio
2.2. Estructura de directorios
La organización de directorios del sistema sigue una estructura que refleja las capas arquitectónicas y facilita la localización de componentes:
/api2_para_consolidar/
├── bootstrap.php # Inicialización del sistema
├── index.php # Punto de entrada principal
├── artesano # CLI para generación de código
├── keys/ # Almacenamiento de claves de seguridad
├── src/
│ ├── ApiLayer/ # Capa de API
│ │ ├── Domains/ # Dominios principales
│ │ │ └── Products/ # Ejemplo de dominio de productos
│ │ │ ├── DTOs/ # Data Transfer Objects
│ │ │ ├── ProductsController.php
│ │ │ ├── ProductsService.php
│ │ │ └── ProductsRoutes.php
│ │ └── Modules/ # Módulos específicos
│ │ ├── Permut/ # Módulo Permut
│ │ │ └── Domains/ # Dominios dentro del módulo
│ │ └── Tpv/ # Módulo TPV
│ │ ├── Domains/ # Dominios dentro del módulo
│ │ │ ├── Carts/
│ │ │ └── Products/
│ │ └── Middlewares/ # Middlewares específicos del módulo
│ ├── App/ # Núcleo de la aplicación
│ │ ├── Base/ # Clases base y abstracciones
│ │ │ ├── DB/ # Componentes de base de datos
│ │ │ ├── Interfaces/ # Interfaces del sistema
│ │ │ ├── Controller.php
│ │ │ ├── DomainController.php
│ │ │ ├── Repository.php
│ │ │ ├── Model.php
│ │ │ └── Service.php
│ │ ├── Commands/ # Comandos CLI para artesano
│ │ ├── Config/ # Configuraciones del sistema
│ │ ├── Core/ # Componentes centrales
│ │ │ ├── Interfaces/ # Interfaces del núcleo
│ │ │ ├── Router/ # Sistema de enrutamiento
│ │ │ ├── QuApp.php # Estado global
│ │ │ ├── QuKernel.php # Kernel de la aplicación
│ │ │ ├── QuContainer.php # Contenedor de dependencias
│ │ │ └── CoreQuartup.php # Puente a sistema legacy
│ │ ├── Dtos/ # Clases base para DTOs
│ │ ├── Exceptions/ # Excepciones personalizadas
│ │ ├── Helpers/ # Funciones y utilidades
│ │ ├── Middlewares/ # Middlewares globales
│ │ └── Validations/ # Sistema de validación
│ └── Infrastructures/ # Capa de infraestructura
│ └── Legacy/ # Integración con sistemas legacy
│ ├── PMaes/ # Módulo PMaes legacy
│ └── PPos/ # Módulo PPos legacy
└── vendor/ # Dependencias externas
Esta estructura refleja claramente la separación de responsabilidades:
- El directorio
ApiLayercontiene los componentes que definen la API pública Appproporciona las abstracciones y funcionalidades del frameworkInfrastructuresgestiona la comunicación con sistemas externos
2.3. Flujo de solicitudes (Request lifecycle)
El ciclo de vida de una solicitud en el sistema sigue un flujo predecible que atraviesa varias capas:
-
Recepción de la solicitud HTTP:
- El punto de entrada
index.phprecibe la solicitud - Se crea una instancia de
RequestutilizandoRequestFactory - Se establece el contexto global mediante
QuApp::setRequest()
- El punto de entrada
-
Inicialización del sistema:
- Se carga la configuración desde
bootstrap.php - Se establece el entorno (DEV/PROD)
- Se registran los servicios globales en el contenedor de dependencias
- Se carga la configuración desde
-
Middleware pipeline:
- Se añaden middleware globales como
TransactionStartMiddleware - Se verifica la autenticación si la ruta lo requiere
- Se procesan otros middleware como
SwitchUtf8Latin1MiddlewareyRateLimitMiddleware
- Se añaden middleware globales como
-
Enrutamiento:
- El
QuRouteranaliza la URL y determina el controlador y método a invocar - Se extraen los parámetros de ruta
- El
-
Resolución de dependencias:
- El
QuContainerresuelve las dependencias necesarias para el controlador - Se aplican las vinculaciones específicas de la ruta
- El
-
Ejecución del controlador:
- Se instancia el controlador adecuado
- Se validan los datos de entrada según las reglas definidas
- Se invoca el método correspondiente al endpoint
-
Lógica de negocio:
- El controlador delega en el servicio de dominio
- El servicio procesa la solicitud, interactuando con repositorios y DTOs
-
Acceso a datos:
- Los repositorios interactúan con modelos para acceder a datos
- Los modelos se comunican con la infraestructura legacy
-
Formación de la respuesta:
- El servicio mapea los datos de salida a través de DTOs
- El controlador construye una respuesta HTTP utilizando
response()->json()
-
Procesamiento de salida:
- Los middleware de salida procesan la respuesta
TransactionEndMiddlewarefinaliza la transacción y registra la operación
-
Envío de la respuesta:
QuApp::sendResponseAndEnd()envía la respuesta al cliente- Se registran logs y se cierra la sesión
Este flujo está diseñado para manejar tanto el caso exitoso como situaciones de error, donde excepciones son capturadas y transformadas en respuestas HTTP apropiadas.
2.4. Sistema de dependencias e inyección
El sistema utiliza un contenedor de inyección de dependencias propio, implementado en QuContainer, que sigue los principios del patrón Service Container.
Funcionamiento del contenedor:
El contenedor permite:
- Registrar servicios para su uso posterior
- Resolver dependencias automáticamente mediante reflection
- Construir objetos con sus dependencias inyectadas
- Aplicar configuraciones específicas por ruta o contexto
Ejemplo de registro de servicios:
// En services.php
return [
Services\Interfaces\MailServiceInterface::class => Services\MailLog::class,
Services\Interfaces\StoreTransactionServiceInterface::class => Services\StoreTransactionLog::class
];
// En index.php
$container = new QuContainer;
foreach ($services as $interface => $implementation) {
$container->set($interface, function () use ($implementation) {
return new $implementation;
});
}
Resolución automática:
El contenedor puede construir objetos mediante análisis de constructor:
// En QuContainer.php
public function build(string $class)
{
$reflector = new ReflectionClass($class);
$constructor = $reflector->getConstructor();
if ($constructor === null) {
return new $class;
}
$parameters = $constructor->getParameters();
$dependencies = [];
foreach ($parameters as $parameter) {
// Resolver dependencia por tipo...
$type = $parameter->getType();
$name = $type->getName();
try {
$dependency = $this->get($name);
} catch (Exception $e) {
$dependency = $this->build($parameter->getType()->getName());
$this->set($name, $dependency);
}
$dependencies[] = $dependency;
}
return $reflector->newInstanceArgs($dependencies);
}
Binding contextual:
El sistema permite registrar bindings específicos para rutas particulares, lo que facilita la implementación de diferentes comportamientos según el contexto:
// En resolvers.php
return [
'/products' => [
InterfaceResDto::class => \ApiLayer\Domains\Products\DTOs\MaesartiProductosDTO::class,
InterfaceReqDto::class => \ApiLayer\Domains\Products\DTOs\CreateMaesartiProductosDTO::class,
],
'/permut/products' => [
InterfaceResDto::class => \ApiLayer\Modules\Permut\Domains\Products\DTOs\MaesartiProductosDTO::class,
InterfaceReqDto::class => \ApiLayer\Modules\Permut\Domains\Products\DTOs\CreateMaesartiProductosDTO::class,
],
];
// En QuKernel::handle()
$path = $request->getPathInfo();
foreach ($resolvers as $routePrefix => $bindings) {
if (strpos($path, $routePrefix) === 0) {
foreach ($bindings as $interface => $concreteClass) {
$this->container->set($interface, function () use ($concreteClass) {
return new $concreteClass();
});
}
break;
}
}
Beneficios del sistema de dependencias:
- Desacoplamiento: Los componentes no necesitan conocer cómo crear sus dependencias
- Testabilidad: Facilita la sustitución de implementaciones reales por mocks en pruebas
- Flexibilidad: Permite cambiar implementaciones sin modificar los consumidores
- Modularidad: Apoya la organización del código en módulos cohesivos y débilmente acoplados
- Extensibilidad: Facilita la ampliación del sistema al permitir reemplazar componentes
La inyección de dependencias es un pilar fundamental de la arquitectura, facilitando la implementación de los demás patrones y el mantenimiento del sistema a largo plazo.