reestructurar en paquetes

This commit is contained in:
David Arranz 2025-04-22 19:14:34 +02:00
parent 6a674a60ec
commit 180a708956
15 changed files with 158 additions and 40 deletions

View File

@ -1,3 +1,5 @@
import { sequelize } from "config/database";
import { initPackages } from "core/package-loader";
import dotenv from "dotenv";
import express, { Application } from "express";
import helmet from "helmet";
@ -5,7 +7,6 @@ import responseTime from "response-time";
import { authProvider } from "./contexts/auth/infraestructure";
import { logger } from "./core/common/infrastructure/logger";
import { globalErrorHandler } from "./core/common/presentation";
import { v1Routes } from "./routes";
dotenv.config();
@ -47,7 +48,8 @@ export function createApp(): Application {
});
// Registrar rutas de la API
app.use("/api/v1", v1Routes());
// app.use("/api/v1", v1Routes());
initPackages(app, sequelize);
// Gestión global de errores.
// Siempre al final de la cadena de middlewares

View File

@ -1,7 +1,6 @@
import { logger } from "core/common/infrastructure/logger";
import dotenv from "dotenv";
import { Sequelize } from "sequelize";
import { registerModels } from "./register-models";
dotenv.config();
@ -40,7 +39,7 @@ export const sequelize = new Sequelize(
export async function connectToDatabase(): Promise<void> {
try {
await sequelize.authenticate();
await registerModels();
//await registerModels();
logger.info(`✔️${" "}Database connection established successfully.`);
} catch (error) {
logger.error("❌ Unable to connect to the database:", error);

View File

@ -1,5 +1,5 @@
import { sequelize } from "@config/database";
import { Transaction } from "sequelize";
import { sequelize } from "../../../../config/database";
import { TransactionManager } from "../database";
export class SequelizeTransactionManager extends TransactionManager {

View File

@ -0,0 +1,55 @@
import { IPackageServer } from "@libs/package";
import { Express } from "express";
import { Sequelize } from "sequelize";
import { registerService } from "./service-registry";
const registeredPackages: Map<string, IPackageServer> = new Map();
const initializedPackages = new Set<string>();
export function registerPackage(pkg: IPackageServer) {
if (registeredPackages.has(pkg.metadata.name)) {
throw new Error(`❌ Paquete "${pkg.metadata.name}" ya registrado.`);
}
registeredPackages.set(pkg.metadata.name, pkg);
}
export function initPackages(app: Express, sequelize: Sequelize) {
for (const [name] of registeredPackages) {
loadPackage(name, app, sequelize);
}
}
function loadPackage(name: string, app: Express, sequelize: Sequelize) {
if (initializedPackages.has(name)) return;
const pkg = registeredPackages.get(name);
if (!pkg) throw new Error(`❌ Paquete "${name}" no encontrado.`);
// Resolver dependencias primero
const deps = pkg.metadata.dependencies || [];
deps.forEach((dep) => loadPackage(dep, app, sequelize));
// Inicializar el package
pkg.init(app);
const pkgApi = pkg.registerDependencies?.();
// Registrar modelos de Sequelize, si los expone
if (pkgApi?.models) {
for (const initModelFn of pkgApi.models) {
initModelFn(sequelize);
}
}
// Registrar sus servicios, si los expone
if (pkgApi?.services) {
const services = pkgApi.services;
if (services && typeof services === "object") {
registerService(pkg.metadata.name, services);
}
}
initializedPackages.add(name);
console.log(`✅ Paquete "${name}" inicializado.`);
}

View File

@ -1,12 +0,0 @@
import { Application } from "express";
import { IPlugin } from "./plugin.interface";
const plugins: IPlugin[] = [];
export function registerPlugin(plugin: IPlugin) {
plugins.push(plugin);
}
export function initPlugins(app: Application) {
plugins.forEach((plugin) => plugin.init(app));
}

View File

@ -1,18 +0,0 @@
import { Application } from "express";
export interface PluginMetadata {
name: string;
version: string;
description?: string;
/**
* Lista de nombres de plugins requeridos por este plugin.
*/
dependencies?: string[];
}
export interface IPlugin {
metadata: PluginMetadata;
init(app: Application): void;
registerDependencies?(): void;
}

View File

@ -0,0 +1,36 @@
const services: Record<string, any> = {};
/**
* Registra un objeto de servicio (API) bajo un nombre.
*/
export function registerService(name: string, api: any) {
if (services[name]) {
throw new Error(`❌ Servicio "${name}" ya fue registrado.`);
}
services[name] = api;
}
/**
* Recupera un servicio registrado, con tipado opcional.
*/
export function getService<T = any>(name: string): T {
const service = services[name];
if (!service) {
throw new Error(`❌ Servicio "${name}" no encontrado.`);
}
return service;
}
/**
* Permite saber si un servicio fue registrado.
*/
export function hasService(name: string): boolean {
return !!services[name];
}
/**
* Devuelve todos los servicios (para depuración o tests).
*/
export function listServices(): string[] {
return Object.keys(services);
}

View File

@ -1,5 +1,6 @@
import http from "http";
import { DateTime } from "luxon";
import { registerPackages } from "packages";
import { createApp } from "./app";
import { ENV } from "./config";
import { connectToDatabase } from "./config/database";
@ -66,6 +67,9 @@ const serverConnection = (conn: any) => {
//const sequelizeConn = createSequelizeAdapter();
//const firebirdConn = createFirebirdAdapter();
// Cargar paquetes de la aplicación
registerPackages();
// Crea el servidor HTTP
const server = http
.createServer(createApp())

View File

@ -0,0 +1,8 @@
// apps/server/src/plugins.ts
//import { ContactsPackage } from '@packages/contacts/server';
//import { InvoicesPackage } from '@packages/invoices/server';
export const registerPackages = () => {
//registerPackage(ContactsPackage);
//registerPackage(InvoicesPackage);
};

View File

@ -8,10 +8,11 @@
"allowSyntheticDefaultImports": true,
"baseUrl": "src",
"paths": {
"@shared/*": ["../../packages/shared/*"],
"@common/*": ["common/*"],
"@contexts/*": ["contexts/*"],
"@config/*": ["config/*"]
"@libs/*": ["../../../libs/*"],
"@packages/*": ["../../../packages/*"]
/*"@common/*": ["common/*"],*/
/*"@config/*": ["config/*"]*/
}
},

View File

@ -1,10 +1,10 @@
//Contrato para los packages backend (Node.js)
import { Express } from "express";
import { PackageMetadata } from "./types";
import { PackageDependencies, PackageMetadata } from "./types";
export interface IPackageServer {
metadata: PackageMetadata;
init(app: Express): void;
registerDependencies?(): void;
registerDependencies?(): PackageDependencies;
}

View File

@ -8,6 +8,7 @@
"@types/react": "^19.1.2",
"express": "^4.18.2",
"react": "^18.3.1",
"sequelize": "^6.37.5",
"typescript": "*"
}
}

View File

@ -1,8 +1,17 @@
// Contiene tipos comunes entre cliente y servidor
import { Model, Sequelize } from "sequelize";
type ModelInitializer = (sequelize: Sequelize) => typeof Model;
export interface PackageMetadata {
name: string;
version: string;
description?: string;
dependencies?: string[];
}
export interface PackageDependencies {
models?: ModelInitializer[];
services?: { [key: string]: any };
}

View File

@ -0,0 +1,30 @@
/* import { getService } from "@apps/server/src/core/service-registry"; */
import { IPackageServer } from "@libs/package";
//import initInvoiceModel from './models/contact.model';
export const InvoicesPackage: IPackageServer = {
metadata: {
name: "invoices",
version: "1.0.0",
dependencies: ["contacts"],
},
init(app) {
// const contacts = getService<ContactsService>("contacts");
app.get("/invoices", (req, res) => {
// const customer = await contacts.getContactById('123');
res.send("Aquí van las facturas");
});
},
registerDependencies() {
return {
models: [
/*initInvoiceModel*/
],
services: {
getInvoice: () => {},
/*...*/
},
};
},
};

View File

@ -283,6 +283,9 @@ importers:
react:
specifier: ^18.3.1
version: 18.3.1
sequelize:
specifier: ^6.37.5
version: 6.37.7(mysql2@3.14.0)
typescript:
specifier: '*'
version: 5.7.3