Repository navigation
Conversation
…cutable El build con @nx/js:swc no reescribía los alias de paths (@app/*, @<app>/*) ni copiaba las libs del workspace, así que node dist/... fallaba con 'Cannot find module'. Ahora cada app se empaqueta con webpack (resuelve alias y embebe libs; node_modules quedan externos) y las entidades de TypeORM se registran explícitamente porque el glob sobre __dirname no encuentra archivos dentro del bundle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mico - OutboxScheduler ejecuta processPendingEvents cada OUTBOX_POLL_INTERVAL_MS - claim optimista PENDING -> PROCESSING con UPDATE condicional (sin doble publicación entre réplicas) - backoff exponencial (next_attempt_at) y FAILED tras OUTBOX_MAX_ATTEMPTS - recupera eventos PROCESSING abandonados tras OUTBOX_PROCESSING_TIMEOUT_MS - tests de éxito, fallo con reintento, FAILED, concurrencia y scheduler Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommentsService.delete ignoraba al solicitante: cualquier usuario autenticado podía borrar cualquier comentario. Ahora devuelve 403 (ForbiddenException) si quien pide no es el autor ni tiene rol ADMIN. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- KafkaModule global: OutboxModule no podía inyectar KafkaService y Nest abortaba el bootstrap de auth/ticket/assignment-service - tipo explícito en columnas nullable (refresh_token, sprint_id, parent_ticket_id): con 'string | null' la metadata es Object y TypeORM rechazaba el esquema en Postgres - spec de assignments con el claim del outbox y OUTBOX_OPTIONS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ningún flujo leía ni escribía en Redis; RedisModule sigue en @app/common. README: diagrama sin Redis, relay del outbox, autorización de comentarios y cómo ejecutar el bundle construido. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problema
OutboxService.processPendingEvents(), así que los eventos se quedaban enPENDINGpara siempre. Además, el paso aPROCESSINGse hacía consave()sin condición, lo que abría la puerta a publicar dos veces si había varias réplicas.@nx/js:swcno reescribía los alias de paths ni copiaba las libs, ynode dist/apps/gateway/src/main.jsfallaba conCannot find module '@gateway/modules/auth/auth.module'. Al arreglarlo aparecieron errores de arranque que ese build roto ocultaba:OutboxModuleno podía inyectarKafkaService(KafkaModuleno era global), así que auth, ticket y assignment-service abortaban el bootstrap.string | nullsintype(refresh_token,sprint_id,parent_ticket_id) hacían que TypeORM rechazara el esquema conDataTypeNotSupportedError.__dirnameno encontraba archivos dentro de un bundle.CommentsService.deleteignoraba al solicitante.Qué cambia
Outbox (
libs/common/src/outbox)OutboxScheduler(dentro deOutboxModule) arranca enonApplicationBootstrapy llama aprocessPendingEvents()cadaOUTBOX_POLL_INTERVAL_MS. Un error en una corrida no detiene el timer, y el timer se limpia en el shutdown.PENDINGaPROCESSINGcon unUPDATEcondicional (id,status,retry_count, vencimiento). Solo quien obtieneaffected = 1publica, así que no hay doble publicación entre réplicas. También hay un guard para que dos corridas no se solapen dentro del mismo proceso.next_attempt_at(base × 2^(n-1), con tope). El evento pasa aFAILEDal llegar aOUTBOX_MAX_ATTEMPTS.PROCESSINGmás deOUTBOX_PROCESSING_TIMEOUT_MS(porque la instancia murió a mitad) se vuelve a reclamar. La entrega es at-least-once.OUTBOX_ENABLEDtrueOUTBOX_POLL_INTERVAL_MSOUTBOX_BATCH_SIZEOUTBOX_MAX_ATTEMPTSOUTBOX_RETRY_BASE_DELAY_MSOUTBOX_RETRY_MAX_DELAY_MSOUTBOX_PROCESSING_TIMEOUT_MSBuild
@nx/webpack:webpack(compiler: tsc, conapps/<app>/webpack.config.jsusandocomposePlugins(withNx())). Es el setup estándar de Nx para apps Nest.node_modulesqueda externo.skipTypeCheckingy el targettypecheckaparte.OutboxEvent.KafkaModuleahora esglobal: true.type: 'varchar'explícito en las columnas nullable.Autorización de comentarios
CommentsService.delete(id, { userId, role })lanzaForbiddenException(403) si quien pide no es el autor niADMIN.Redis
RedisModule.registerde los 5AppModule, porque ningún flujo lo usaba y solo agregaba una dependencia de arranque.RedisModulesigue en@app/commony Redis endocker-compose.yml.Cómo se usa
npm run infra:up npx nx build ticket-service node dist/apps/ticket-service/main.js # desde la raíz del repoEl relay arranca solo con cada servicio que importa
OutboxModule. Para desactivarlo:OUTBOX_ENABLED=false.Casos borde
outbox_events.next_attempt_at(nullable). Hoy el esquema se crea consynchronizeendevelopmenty el repo no tiene migraciones. En un entorno consynchronize: falsehay que crearla a mano:ALTER TABLE outbox_events ADD COLUMN next_attempt_at timestamp NULL;.SENT, el evento se reenvía después del timeout. Los consumidores deben ser idempotentes poreventId.x-user-id, y ticket-service no tiene estrategia que llenerequest.usera partir de esa cabecera. Este PR aplica la regla autor/ADMIN sobrerequest.user, pero la propagación de identidad entre gateway y servicios es un problema previo que queda fuera de alcance.master. Lo verifiqué mergeándolo localmente sobre feat(deps): actualizar Nx 23 y NestJS 11 #11: merge sin conflictos y todo en verde. Nx 23 marca@nx/webpack:webpack,composePlugins/withNxyskipTypeCheckingcomo deprecated para Nx 24, igual que los executors de jest y eslint que ya existen. La migración a targets inferidos queda para un PR posterior.Validación
npm cilimpio y luegonpx nx run-many -t lint typecheck test build --skip-nx-cache: OK en los 7 proyectos. Tests: 92 (antes 71).FAILEDal llegar al máximo de intentos.FAILEDtras 5 intentos.PROCESSINGabandonado se reclama después del timeout.gateway: arranca y escucha en:3000.ticket-serviceyauth-servicecontra Postgres: conectan,synchronizecrea las tablas (incluidaoutbox_events.next_attempt_at) y fallan solo por Kafka (KafkaJSNumberOfRetriesExceeded).Unable to connect to the database.notification-service: falla solo por Kafka.Cannot find module '@gateway/modules/auth/auth.module'.🤖 Generated with Claude Code