# 8. Decisiones de diseño Registro breve de las decisiones que más condicionan el código, con su motivo y su coste. ### Arquitectura hexagonal con una librería Nx por capa Las reglas de dependencia las comprueba el lint, no la disciplina del equipo. **Coste**: más librerías y más ficheros (un command y un handler por fichero, como en el patrón de referencia del equipo). ### La misma estructura en API y web `libs/api//` y `libs/web//`: quien conoce un lado se orienta en el otro. ### Event sourcing en PostgreSQL (y no en MongoDB) El requisito era escritura en PostgreSQL y lectura en MongoDB. El event store (`events`) implementa la misma API que el `MongoEventStore` del patrón de referencia (`persist`, `getEventsByStreamId`). **Diferencia**: un conflicto de versión lanza `ConcurrencyError` en lugar de solo registrarse, para no perder cambios en silencio. ### `commitAndWait()` El `commit()` de `@nestjs/cqrs` no devuelve la promesa del publisher: el command respondería OK aunque el event store fallara. `VersionedAggregateRoot.commitAndWait()` espera la escritura y avanza la versión del agregado. ### Redis entre escritura y lectura, y un bridge que espera El requisito era que Redis llevase los datos de escritura a lectura. El `EventsBridge` hace el papel del _change stream_ del patrón de referencia, pero no usa `eventBus.subject$.next()`: así los handlers se ejecutarían en paralelo (`mergeMap`), un `IssueStatusChanged` podría proyectarse antes que su `IssueCreated` y un fallo se perdería. El bridge espera a cada `@EventsHandler` en orden y confirma en Redis (`XACK`) al final. ### `EventClsRegistry` y `@AutowiredEvent` en el dominio En el patrón de referencia están en infraestructura, pero los eventos de dominio los usan; ponerlos allí rompería la regla de capas. ### Lookups fuera de los agregados La unicidad de claves de proyecto, la numeración de tickets y los nombres de release no caben en un agregado. Se resuelven con tablas pequeñas en PostgreSQL. **Coste**: no forman parte de la misma transacción que los eventos (una clave reservada puede quedar huérfana si el guardado falla después). ### Reglas críticas contra el event store, no contra Mongo "No publicar una release con tickets sin terminar" se comprobaba primero contra el read model y fallaba si alguien metía un ticket y publicaba justo después. Ahora reconstruye esos tickets desde los eventos. ### Proxy de tokens en la API Pireon no permite CORS en `/oauth/token` y el `client_secret` no debe llegar al navegador. `POST /api/auth/token` hace de intermediario, igual que la API de administración de la landing. ### Caché de Apollo desactivada en la web El estado vive en los stores de NgRx (alimentados por queries y subscriptions). Tener dos fuentes de verdad (Apollo cache y stores) solo añadiría inconsistencias. ### Estado web con el plugin de eventos de NgRx Signals Los efectos se declaran con `withEventHandlers` (nombre en v22 de lo que antes era `withEffects`), y lo que emiten se despacha automáticamente. ### Facades delante de los stores Las páginas usan una facade por store (`BoardFacade`, `IssueFacade`…) y no conocen ni los stores ni los eventos, que ya no se exportan. La facade expone el estado como señales y las intenciones como métodos, y pone el contexto (id del proyecto o del ticket abierto). Así se puede cambiar cómo se gestiona el estado sin tocar las páginas, y la lógica de presentación se prueba sin plantillas. **Coste**: una clase más por store, casi toda de reenvío. ### Imagen de la API con dependencias propias El `package.json` raíz incluye Angular y `@pi/ui` (del Nexus privado). La imagen de la API usa `apps/api/package.json`, así que ni lleva el frontend ni necesita el token del Nexus en ejecución.