Los ejemplos de código llegarán en una revisión posterior.
En esta página
Propiedades de arquitectura tratadas
- P-1Un esquema en tiempo de ejecución como contrato, el tipo estático derivado de él y, en el patrón de referencia, una factoría como único camino hacia un valor verificado.
- P-4Contexto de OpenTelemetry a través de AsyncLocalStorage, y la identidad de auditoría como campo obligatorio de cada mensaje y de cada job.
- P-5La procedencia declarada en el esquema y preservada por el serializador de respuestas.
- P-6Reglas de dependencias y comprobaciones de esquemas en la integración continua, cada una con un fallo introducido a propósito.
- P-7Puertos en el paquete de dominio; un paquete adaptador por sistema externo.
- P-8Una interfaz de políticas sobre un motor, por HTTP o compilado a WebAssembly, con la revisión de la política en cada decisión.
- P-2, P-3Solo con la profundidad que publica COADF.
Intención de arquitectura
TypeScript da a la frontera un vocabulario y nada de su aplicación. Las anotaciones de tipo nunca cambian el comportamiento en tiempo de ejecución de un programa: describen lo que un valor debería ser y desaparecen antes de que se ejecute, mientras que los datos que llegan de un modelo, de una cola o de una petición tienen la forma que tengan. Los tipados estándar declaran el resultado de JSON.parse como any, que el compilador deja tratar luego como cualquier cosa.
Por eso, en este ecosistema la frontera es el esquema en tiempo de ejecución, y el tipo estático se deriva del esquema, nunca al revés. Todo lo demás en este perfil se sigue de esa inversión. Zod, Ajv, Fastify y NestJS son ejemplos: COADF no exige ninguno de ellos, y cualquier validador en tiempo de ejecución que rechace lo que el contrato no declara mantiene la propiedad.
Correspondencia tecnológica
| Propiedad de arquitectura | TypeScript y Node |
|---|---|
| Tipo de frontera | Un esquema en tiempo de ejecución (Zod, o JSON Schema con Ajv) como fuente, con el tipo estático inferido de él. El sistema de tipos de TypeScript es estructural, así que un valor verificado que no debe confundirse con una propuesta necesita una marca (brand), y solo una factoría la aplica. |
| Validación en tiempo de ejecución | parse o safeParse en cada lado receptor. parse de Zod lanza una excepción ante datos no válidos y safeParse devuelve en su lugar un resultado. |
| Claves desconocidas | Una elección explícita por esquema. En Zod 4, z.object() elimina las claves desconocidas y z.strictObject() las rechaza. |
| Validación del framework | Esquemas de ruta de Fastify, validados con Ajv y serializados con fast-json-stringify; o el ValidationPipe de NestJS con whitelist y forbidNonWhitelisted. |
| Regla de arquitectura | Reglas de dependencias comprobadas en la CI: dependency-cruiser, reglas de fronteras de ESLint o project references de TypeScript que convierten una importación ilegal en un error de compilación. |
| Contexto de ejecución | OpenTelemetry JS, cuyo gestor de contexto para Node está basado en AsyncLocalStorage. |
| Traza de auditoría | El mismo esquema de PostgreSQL a través de un driver; entradas escritas en la transacción de negocio. |
| Aislamiento de normas | Interfaces de puerto en el paquete de dominio; cada cliente de proveedor confinado a un paquete adaptador del workspace. |
| Política | Open Policy Agent por HTTP, o Rego compilado a WebAssembly y evaluado dentro del proceso mediante el SDK de JavaScript de OPA; la revisión de la política en cada decisión. |
Patrón de referencia
Los ejemplos de código llegarán en una revisión posterior. Se aplazan hasta que los haya leído una persona revisora que trabaje en este ecosistema, en lugar de escribirlos para que los cuatro perfiles se parezcan. La estructura que sigue es el patrón de referencia que implementarán esos ejemplos.
Paquetes
schemascontiene los esquemas en tiempo de ejecución de cada contrato: la propuesta con su procedencia, la entrada de auditoría con los campos publicados, la decisión de política con su revisión. Todos los demás paquetes importan de aquí sus tipos, inferidos de los esquemas.inferencellama al modelo y devuelve el resultado de analizar la respuesta con el esquema de propuesta. No puede importarrecordsniaudit, y una regla de dependencias en la CI lo afirma.recordsexporta una factoría que recibe una propuesta y la verificación de una persona con nombre y devuelve un valor verificado con marca. El tipo de la marca no puede construirse en otro lugar sin un cast, y una regla de lint prohíbe ese cast fuera de este paquete.audit,classificationcon su adaptador del proveedor, ypolicysiguen la misma forma que en los demás perfiles.
Fronteras en tiempo de ejecución
Cada lado receptor analiza los datos: la ruta HTTP, el consumidor de mensajes, el ejecutor de jobs y la factoría del dominio. Un esquema de ruta en Fastify o un pipe en NestJS cubre solo el punto de entrada web.
El SDK de OpenTelemetry se carga antes que los módulos propios de la aplicación. Una aplicación con módulos ES necesita el loader hook de OpenTelemetry para la instrumentación automática, además de precargar el SDK.
Modos de fallo
Una anotación de tipo tomada por validación
Hacer un cast del resultado de
JSON.parseal tipo de propuesta compila, y no comprueba nada. El cast es la frontera más habitual en el código TypeScript, y no es una frontera.Eliminar tomado por rechazar
Un objeto de Zod por defecto elimina las claves desconocidas. Una respuesta que afirma estar verificada no se rechaza; la afirmación se quita en silencio, y con ella se pierde el hecho de que el modelo afirmó de más. Donde la propia afirmación es evidencia, hay que usar un objeto estricto y registrar el rechazo.
Conversión por defecto en Fastify
Las opciones de Ajv por defecto de Fastify incluyen
coerceTypes,useDefaultsyremoveAdditional. Un número enviado como texto llega como número, y un valor ausente puede llegar como valor por defecto. Hay que configurar el validador explícitamente para las rutas de frontera.Procedencia descartada por el serializador de respuestas
Con un esquema de respuesta, las propiedades que el esquema no enumera se omiten, salvo que admita propiedades adicionales. Un esquema de respuesta escrito para la pantalla, sin la procedencia, la elimina de todas las respuestas.
Un pipe de NestJS que lo deja pasar todo
whitelistelimina las propiedades sin decoradores de validación, yforbidNonWhitelistedlanza una excepción en lugar de eliminarlas. Sin ninguno de los dos, una propiedad que el DTO no declara llega al handler.Contexto perdido a través del trabajo asíncrono
AsyncLocalStorageconserva datos durante la vida de una petición u otra duración asíncrona, y sigue las continuaciones que Node puede ver. El trabajo que vuelve a entrar desde un callback de un pool o desde la cola propia de una biblioteca puede llegar sin él; la identidad de auditoría tiene que ir en el mensaje, no recuperarse del contexto.Instrumentación automática ausente sin aviso
Un servicio con módulos ES arrancado sin el loader hook funciona con normalidad, y las bibliotecas que el hook habría instrumentado no producen spans. Nada falla; la traza simplemente es más corta que la ejecución.
Una marca falsificada con un cast
Un cast al tipo verificado compila en cualquier sitio. La marca más la factoría es una convención que el compilador no puede imponer por sí solo; lo que la hace cumplirse son la regla de lint y la revisión de código.
Dos versiones de los esquemas compartidos
Productor y consumidor dependen de versiones distintas del paquete
schemas, y cada uno valida correctamente contra un contrato distinto. Lo detectan las pruebas de contrato entre las versiones realmente desplegadas; las pruebas unitarias no.
Verificación
Prueba unitaria
Pasa cuando: El esquema de propuesta rechaza las respuestas mal formadas: clave desconocida, tipo erróneo, procedencia ausente, atributo inesperado.
Prueba de dientes: Sustituir el objeto estricto por uno por defecto en una rama desechable: la prueba de la clave desconocida falla.
Prueba unitaria
Pasa cuando: Una prueba a nivel de tipos afirma que una propuesta no es asignable a un valor verificado.
Prueba de dientes: Quitar la marca: la prueba a nivel de tipos deja de compilar.
Prueba de arquitectura
Pasa cuando: Las reglas de dependencias pasan: ninguna importación de
inferencehaciarecordsoaudit, ningún cliente de proveedor fuera de su adaptador.Prueba de dientes: Introducir a propósito la importación prohibida: la CI falla.
Prueba de contrato
Pasa cuando: La respuesta de cada ruta que sirve una propuesta contiene los campos de procedencia.
Prueba de dientes: Quitar una propiedad de procedencia del esquema de respuesta: la prueba falla, porque el serializador la descartó.
Prueba de integración
Pasa cuando: Con un exportador de spans en memoria, el trabajo asíncrono aparece en la traza de la petición, y las entradas de auditoría persistidas llevan una sola identidad.
Prueba de integración
Pasa cuando: Las decisiones de política llevan la revisión del material de políticas que las produjo; las respuestas sin ella se rechazan.
Realizaciones alternativas
- Otras bibliotecas de esquemas (Valibot, TypeBox con Ajv, io-ts). La propiedad es un esquema en tiempo de ejecución que es la fuente del tipo; la biblioteca es una elección.
- Transportes con el esquema primero, como GraphQL o Protocol Buffers, donde el contrato es un artefacto separado a partir del cual se generan ambos lados.
- Un motor de políticas dentro del proceso mediante WebAssembly, en lugar de por HTTP, para eliminar el salto de red; la exigencia de registrar la revisión de la política no cambia.
Compromisos
- Poner el esquema primero desplaza la fuente de verdad fuera de las declaraciones de tipos, algo que a algunos equipos les resulta poco natural; poner el tipo primero deja el tiempo de ejecución sin protección.
- Los validadores compilados son rápidos y traen valores por defecto que convierten y rellenan; la configuración más rápida no es la más estricta.
- Los objetos estrictos rechazan más de lo que produce un modelo, y cada rechazo exige una decisión sobre qué registrar.
- Un workspace de paquetes pequeños hace expresables las reglas de dependencias, y cuesta maquinaria de build y de publicación de versiones.
Limitaciones
- En Companion 1.0 no se publica código para este perfil, y no se ejecutó ningún ejemplo. El comportamiento descrito procede de la documentación oficial vigente, comprobada el 11 de septiembre de 2026, para Zod 4, Fastify 5, NestJS, Node.js 24 y OpenTelemetry JS 2.
- Nada de lo que aquí figura muestra cómo se representa la confianza, cuándo se requiere revisión ni cómo se organiza la revisión; COADF no publica esas partes de P-2 y P-3.
Lo que este perfil no establece
Seguir este perfil no establece cumplimiento normativo, certificación ni evaluación de la conformidad, y COADF no exige ni TypeScript ni Node.
Entorno de referencia probado
No se ejecutó ningún ejemplo para este perfil.
Fuentes
- TypeScript: The Basics: erased types · documentación oficial · Comprobado el 2026-09-11
- TypeScript: lib.es5.d.ts: JSON.parse · repositorio del proyecto · Comprobado el 2026-09-11
- TypeScript: Type compatibility · documentación oficial · Comprobado el 2026-09-11
- Zod: Basic usage: handling errors · documentación oficial · Zod 4 · Comprobado el 2026-09-11
- Zod: Objects and z.strictObject · documentación oficial · Zod 4 · Comprobado el 2026-09-11
- Fastify: Validation and serialization · documentación oficial · Fastify 5.12 · Comprobado el 2026-09-11
- Fastify: fast-json-stringify: additionalProperties · repositorio del proyecto · Comprobado el 2026-09-11
- NestJS: Validation: stripping properties · documentación oficial · Comprobado el 2026-09-11
- Node.js: Asynchronous context tracking: AsyncLocalStorage · documentación oficial · Node.js 24 · Comprobado el 2026-09-11
- OpenTelemetry: Context manager (JavaScript) · repositorio del proyecto · OpenTelemetry JS 2.11.0 · Comprobado el 2026-09-11
- OpenTelemetry JS: ESM support · repositorio del proyecto · Comprobado el 2026-09-11
- Open Policy Agent: WebAssembly · documentación oficial · OPA 1.20 · Comprobado el 2026-09-11
