Cómo integrar un ERP en CodeIgniter 4 con una tienda Angular sobre PostgreSQL Cloud

Gracias por darme amor compartiendo en tu app favorita:

La integración de un sistema de Planificación de Recursos Empresariales (ERP) con una tienda online es crucial para la eficiencia y la coherencia de los datos en cualquier negocio moderno.

Este artículo explora cómo lograr esta integración utilizando CodeIgniter 4 para el ERP interno, Angular para la tienda online, y una base de datos PostgreSQL en la nube como núcleo central.

Además, profundizaremos en la sincronización con sistemas de terceros y en aspectos clave como la seguridad, el rendimiento y el manejo de errores.


1. Estructura del entorno general

Para entender la integración, primero visualicemos la arquitectura:

  • ERP Interno (CodeIgniter 4): Este sistema gestiona procesos como la fabricación, el inventario, los pedidos internos y la contabilidad. Se alojará en la intranet corporativa, asegurando acceso exclusivo para el personal autorizado. Se conectará a la base de datos PostgreSQL alojada en la nube.
  • Tienda Online (Angular v20): Desarrollada con la última versión de Angular, esta tienda será accesible públicamente en internet. También se conectará a la misma base de datos PostgreSQL en la nube, compartiendo información en tiempo real con el ERP.
  • Sistemas de Terceros: Aplicaciones externas (como un CRM, un sistema de facturación o una plataforma de logística) que requieren sincronización de datos (productos, stock, pedidos). La conexión con estos sistemas se realizará a través de sus APIs, generalmente mediante scripts programados.

El uso de PostgreSQL en la nube ofrece alta disponibilidad, escalabilidad y accesibilidad desde diferentes ubicaciones, lo cual es fundamental para una integración fluida entre sistemas distribuidos.


2. Integración centralizada con PostgreSQL Cloud

La clave de esta integración es la base de datos PostgreSQL unificada y centralizada en la nube. Esto permite que tanto el ERP como la tienda online accedan a los mismos datos en tiempo real, eliminando la necesidad de sincronizaciones complejas entre bases de datos separadas.

2.1. Diseño unificado de la base de datos

  • Normalización: Asegúrate de que las tablas estén bien normalizadas para evitar redundancia y mejorar la integridad de los datos.
  • Campos compatibles: Define campos con tipos de datos compatibles para todas las entidades que serán compartidas (productos, usuarios, pedidos, stock, etc.). Es vital que tanto CodeIgniter 4 como Angular (a través de su backend) puedan interpretar y manipular estos datos sin conflictos.
  • Estrategia de indexación: Diseña una estrategia de indexación robusta. Identifica las columnas que se usan frecuentemente en cláusulas WHERE, JOIN u ORDER BY y crea índices adecuados. Esto mejorará drásticamente el rendimiento de las consultas, especialmente a medida que la base de datos crezca.
// Ejemplo en app/Config/Database.php
public array $default = [
    'DSN'      => '',
    'hostname' => 'tu_host_postgresql_cloud',
    'username' => 'tu_usuario',
    'password' => 'tu_password',
    'database' => 'tu_nombre_db',
    'DBDriver' => 'Postgre',
    'DBPrefix' => '',
    'pConnect' => false,
    'DBDebug'  => (ENVIRONMENT !== 'production'),
    'charset'  => 'utf8',
    'DBCollat' => 'utf8_general_ci',
    'swapPre'  => '',
    'encrypt'  => false, // Considera true si tu proveedor lo soporta nativamente
    'compress' => false,
    'strictOn' => false,
    'failover' => [],
    'port'     => 5432,
];

2.2. Configuración de Conexión

  • CodeIgniter 4: Configura la conexión a la base de datos en el archivo app/Config/Database.php, utilizando las credenciales y el host de tu instancia de PostgreSQL en la nube.
  • Angular (Backend): La tienda Angular se comunicará con un backend (probablemente también en PHP/Node.js/Python) que a su vez se conectará a la base de datos. Este backend será responsable de las operaciones CRUD y la lógica de negocio.

3. Seguridad de la base de datos en la nube

La seguridad es primordial cuando se expone una base de datos en la nube. No basta con mencionar TLS; hay que ser proactivo.

  • TLS/SSL: Asegúrate de que todas las conexiones a la base de datos utilicen TLS/SSL (Transport Layer Security) para cifrar los datos en tránsito. La mayoría de los proveedores de nube lo habilitan por defecto o facilitan su configuración.
  • Control de acceso a nivel de aplicación (y de red):
    • Firewalls de base de datos: Configura firewalls a nivel de tu proveedor de nube (ej., grupos de seguridad en AWS, reglas de firewall en Google Cloud, Azure Network Security Groups) para restringir el acceso a la base de datos únicamente a las direcciones IP de tus servidores de ERP y tienda online. ¡Nunca expongas la base de datos directamente a Internet!
    • Usuarios con privilegios mínimos: Crea usuarios de base de datos con los privilegios mínimos necesarios para cada aplicación. Por ejemplo, el usuario de la tienda online solo debería tener permisos de lectura en tablas de productos y escritura en tablas de pedidos. El usuario del ERP tendrá permisos más amplios.
  • Gestión segura de credenciales:
    • Variables de entorno: Nunca almacenes credenciales directamente en el código fuente. Utiliza variables de entorno en el servidor donde se ejecuta CodeIgniter y el backend de Angular.
    • Servicios de gestión de secretos: Para entornos de producción, considera el uso de servicios de gestión de secretos (ej., AWS Secrets Manager, Google Cloud Secret Manager, HashiCorp Vault) para almacenar y rotar las credenciales de forma segura.
  • Auditoría y monitoreo: Habilita el registro de auditoría en tu instancia de PostgreSQL en la nube para monitorear quién accede a qué y cuándo. Configura alertas para actividades sospechosas o intentos de acceso no autorizados.
  • Backups y recuperación: Implementa una política de copias de seguridad regular y automatizada. Asegúrate de que tu proveedor de nube realice backups frecuentes y que tengas un plan probado de recuperación ante desastres.

4. Sincronización con sistemas de terceros (APIs)

Cuando la integración directa a la base de datos no es posible (lo cual es lo común con sistemas de terceros), las APIs son la solución.

4.1. El rol de los scripts y cron jobs

  • Scripts dedicados: Desarrolla scripts específicos (preferiblemente en PHP para CodeIgniter 4 o Python) que interactúen con las APIs de los sistemas de terceros.
    • GET: Para consultar productos, stock, listas de precios.
    • POST/PUT: Para enviar nuevos pedidos, actualizar estados de envío, sincronizar clientes.
  • Ejecución con Cron Jobs: Programa estos scripts para que se ejecuten periódicamente mediante tareas cron en tu servidor. La frecuencia dependerá de la necesidad de actualización (cada hora para stock, cada 5 minutos para nuevos pedidos).

4.2. Ejemplo de script de sincronización en CodeIgniter 4

Crearemos un «comando» en CodeIgniter 4 para sincronizar productos desde una API externa.

  • Crear un comando personalizado:
php spark make:command SincronizarProductosExternos
  • Editar app/Commands/SincronizarProductosExternos.php:
<?php namespace App\Commands;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;

class SincronizarProductosExternos extends BaseCommand
{
    protected $group        = 'custom';
    protected $name         = 'sinc:productos';
    protected $description  = 'Sincroniza productos desde una API externa con la base de datos.';

    public function run(array $params)
    {
        CLI::write('Iniciando sincronización de productos externos...');

        try {
            // 1. Configurar la URL de la API externa
            $apiUrl = 'https://api.externa.com/productos';
            $apiKey = getenv('API_EXTERNA_KEY'); // Obtener de variable de entorno

            // 2. Realizar la solicitud HTTP (usando el cliente HTTP de CodeIgniter)
            $client = \Config\Services::curlrequest([
                'base_uri' => $apiUrl,
                'timeout'  => 10,
            ]);

            $response = $client->get('/', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiKey,
                    'Accept'        => 'application/json',
                ],
            ]);

            // 3. Manejo de Errores Robustos en la Respuesta de la API
            if ($response->getStatusCode() !== 200) {
                CLI::error('Error al obtener productos de la API externa. Código: ' . $response->getStatusCode());
                CLI::error('Mensaje: ' . $response->getBody());
                return; // Detener la ejecución si hay un error
            }

            $productosExternos = json_decode($response->getBody(), true);

            if (empty($productosExternos)) {
                CLI::write('No se encontraron productos para sincronizar.');
                return;
            }

            // Cargar el modelo de productos de tu aplicación
            $productoModel = new \App\Models\ProductoModel();
            $db = \Config\Database::connect(); // Obtener la conexión a la base de datos

            // 4. Procesar y Actualizar Productos en una Transacción
            $db->transStart(); // Iniciar transacción
            $actualizados = 0;
            $nuevos = 0;

            foreach ($productosExternos as $productoData) {
                // Mapear los datos de la API a tu esquema de base de datos
                $dataToSave = [
                    'sku'           => $productoData['codigo_sku'],
                    'nombre'        => $productoData['nombre_producto'],
                    'descripcion'   => $productoData['descripcion'],
                    'precio'        => $productoData['precio_unitario'],
                    'stock'         => $productoData['cantidad_disponible'],
                    // ... otros campos
                ];

                // Intentar encontrar el producto por SKU o ID externo
                $productoExistente = $productoModel->where('sku', $dataToSave['sku'])->first();

                if ($productoExistente) {
                    // Actualizar producto existente
                    $productoModel->update($productoExistente['id'], $dataToSave);
                    $actualizados++;
                } else {
                    // Crear nuevo producto
                    $productoModel->insert($dataToSave);
                    $nuevos++;
                }
            }

            $db->transComplete(); // Completar transacción (commit o rollback)

            if ($db->transStatus() === false) {
                CLI::error('Error de transacción al sincronizar productos. Se hizo un rollback.');
            } else {
                CLI::write("Sincronización completada. Productos nuevos: {$nuevos}, Productos actualizados: {$actualizados}.");
            }

        } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) {
            CLI::error('Error de red o conexión HTTP: ' . $e->getMessage());
        } catch (\Exception $e) {
            CLI::error('Ocurrió un error inesperado: ' . $e->getMessage());
            // Logear el error completo para depuración
            log_message('error', 'Error en sincronización de productos: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
        }

        CLI::write('Sincronización de productos externos finalizada.');
    }
}

4.3. Configurar tarea cron

En el servidor donde se ejecuta tu ERP CodeIgniter 4, edita el archivo crontab (ej. crontab -e) y añade una línea como esta:

# Sincroniza productos cada hora
0 * * * * /usr/bin/php /ruta/a/tu/proyecto/public/index.php sinc:productos >> /var/log/sinc_productos.log 2>&1

Asegúrate de que la ruta a tu proyecto sea correcta y de que el usuario que ejecuta el cron tenga permisos.


5. Consideraciones de rendimiento y escalabilidad

Una base de datos centralizada es excelente, pero su rendimiento puede convertirse en un cuello de botella si no se gestiona bien.

  • Optimización de consultas:
    • Análisis con EXPLAIN ANALYZE: Utiliza la herramienta EXPLAIN ANALYZE de PostgreSQL para entender cómo se ejecutan tus consultas y dónde pueden optimizarse.
    • Evita SELECT *: Selecciona solo las columnas que realmente necesitas.
    • JOIN Eficientes: Asegúrate de que las condiciones de JOIN utilicen columnas indexadas.
  • Pool de conexiones: Configura un pool de conexiones adecuado tanto para CodeIgniter como para el backend de Angular. Esto reduce la sobrecarga de establecer y cerrar conexiones repetidamente a la base de datos, mejorando el rendimiento en cargas altas.
  • Particionamiento de tablas: Para tablas con un volumen de datos extremadamente alto (ej., registros de logs, historial de pedidos antiguos), considera el particionamiento de tablas. Esto divide una tabla grande en partes más pequeñas y manejables, lo que puede mejorar el rendimiento de las consultas y el mantenimiento.
  • Escalabilidad de la instancia de Cloud PostgreSQL: Tu proveedor de nube ofrece opciones para escalar verticalmente (aumentar CPU, RAM) o incluso horizontalmente (réplicas de lectura) tu instancia de PostgreSQL. Monitorea el rendimiento de la base de datos y escala según sea necesario.

6. Alternativas avanzadas a la sincronización con cron jobs

Si bien los cron jobs son un buen punto de partida, para sistemas con alta demanda o necesidad de reacción instantánea, existen soluciones más avanzadas.

  • Arquitectura basada en eventos / colas de mensajes:
    • Concepto: En lugar de que un sistema «pregunte» periódicamente al otro si hay cambios, un sistema «publica» un evento (ej., «nuevo pedido creado», «stock actualizado») en una cola de mensajes. Otro sistema «se suscribe» a esa cola y reacciona cuando recibe un evento.
    • Herramientas: Tecnologías como RabbitMQ, Apache Kafka, o servicios gestionados en la nube como AWS SQS/SNS, Google Cloud Pub/Sub o Azure Service Bus son ideales para esto. Ofrecen desacomplamiento, resiliencia y escalabilidad.
  • Webhooks: Cuando los sistemas de terceros ofrecen esta funcionalidad, son una alternativa eficiente a los cron jobs. Un webhook permite que el sistema de terceros notifique directamente a tu ERP o tienda online (a través de una URL específica) cuando ocurre un evento relevante (ej., un pago procesado, un envío completado), activando así tu lógica de sincronización.

7. Coherencia de datos y experiencia de usuario

En un entorno de integración distribuida, es importante considerar cómo la sincronización afecta la percepción del usuario.

  • Coherencia eventual: En sistemas asíncronos, los datos pueden no estar perfectamente sincronizados en todo momento; esto se conoce como «coherencia eventual». Por ejemplo, un producto recién vendido puede tardar unos segundos en reflejarse en el inventario de la tienda online si la sincronización es asíncrona.
  • Manejo en la UI:
    • Mensajes al Usuario: Si el stock o un precio pueden tardar en actualizarse, considera mostrar un mensaje como «La disponibilidad del producto se actualiza cada X minutos».
    • Indicadores de carga: Utiliza indicadores visuales de carga para operaciones que dependen de la sincronización.
    • Notificaciones: En casos críticos, notifica al usuario si una operación (ej., realizar un pedido) no pudo completarse debido a un problema de sincronización.

Capacidad para ver «el mapa»

La integración de un ERP basado en CodeIgniter 4 con una tienda Angular sobre una base de datos PostgreSQL en la nube es una estrategia robusta y eficiente. Una base de datos unificada facilita la coherencia de los datos en tiempo real, mientras que la sincronización vía APIs con sistemas de terceros, apoyada por scripts y tareas cron (o alternativas más avanzadas), asegura que todos los datos estén alineados.

Al prestar especial atención a la seguridad de la base de datos, optimizar el rendimiento, planificar la escalabilidad y gestionar la coherencia eventual de los datos, podrás construir un ecosistema de negocio integrado que impulse la eficiencia operativa y mejore la experiencia del cliente.

La flexibilidad de CodeIgniter 4 como backend y la potencia de Angular para el frontend, combinadas con la robustez de PostgreSQL en la nube, forman una base sólida para el éxito de tu proyecto de integración.

¡Ah!

Y como punto final…

¡RECUERDA DOCUMENTARLO TODO!