Los ejemplos de código llegarán en una revisión posterior.
En esta página
Propiedades de arquitectura tratadas
- P-1Una jerarquía sellada que separa las propuestas de los valores verificados, validación en la frontera web y reglas de módulos que el build verifica.
- P-4Micrometer Observation y Tracing para el contexto de ejecución, la identidad de auditoría en cada contrato y entradas de auditoría escritas dentro de la transacción de negocio.
- P-5La procedencia como campos de los tipos de valor, serializada de forma explícita.
- P-6Verificación de la arquitectura en la fase de pruebas, y checks obligatorios que no pueden pasar por haberse omitido.
- P-7Puertos en el módulo de dominio y adaptadores en módulos propios.
- P-8Una interfaz de políticas con un adaptador para el motor, y decisiones registradas con su revisión de la política.
- P-2, P-3Solo con la profundidad que publica COADF.
Intención de arquitectura
La fuerza de Spring para estas propiedades es la declaración: restricciones sobre tipos, transacciones y validación aplicadas por el contenedor, observaciones configuradas una sola vez, reglas de módulos comprobadas por una prueba. Su fallo característico es la otra cara del mismo diseño. Una declaración se cumple en las rutas que el contenedor intercepta, a través de un proxy, en un binding web o en un executor que él configuró, y en ningún otro sitio. Una propiedad escrita como anotación es tan fuerte como el conjunto de llamadas que realmente pasan por Spring, y no más.
Este perfil hace corresponder cada propiedad con Spring Boot 4 y Spring Framework 7, la generación vigente cuando se escribió, y nombra los lugares donde una propiedad declarada deja de aplicarse sin hacer ruido.
Correspondencia tecnológica
| Propiedad de arquitectura | Java y Spring |
|---|---|
| Tipo de frontera | Una interfaz sealed con implementaciones record: un tipo de propuesta y un tipo verificado, construidos en módulos distintos. Los records son portadores de datos con inmutabilidad superficial: un componente que es una lista mutable sigue siendo mutable, así que hay que copiarlo en el constructor. Las clases selladas son una característica estándar desde Java 17. |
| Validación en tiempo de ejecución | Restricciones de Jakarta Validation en los tipos de petición, activadas con @Valid. Una validación fallida de @RequestBody lanza MethodArgumentNotValidException, que por defecto se responde con 400. |
| Campos desconocidos | Una decisión explícita y probada en los tipos de frontera sobre las propiedades que el tipo no declara, en lugar de lo que diga en ese momento la configuración del mapeador JSON. |
| Regla de arquitectura | ApplicationModules.verify() de Spring Modulith, o reglas de ArchUnit escritas como pruebas unitarias. |
| Contexto de ejecución | Micrometer Observation y Micrometer Tracing, con puente hacia OpenTelemetry. |
| Contexto asíncrono | ContextPropagatingTaskDecorator con la biblioteca context-propagation de Micrometer; para el executor autoconfigurado, la propiedad de Boot spring.task.execution.propagate-context. |
| Traza de auditoría | Entradas escritas en la transacción de negocio; permisos y triggers de base de datos como en el ejemplo de PostgreSQL; el registro de publicación de eventos de Spring Modulith para los eventos que no deben perderse. |
| Aislamiento de normas | Interfaces de puerto en el módulo de dominio; un módulo adaptador por sistema externo; el cliente del proveedor nunca visible para el dominio. |
| Política | Una interfaz de políticas propiedad del dominio con un adaptador para Open Policy Agent, o tablas de reglas; la revisión de la política guardada en la decisión. |
| Pruebas de integración | Un PostgreSQL real a través de Testcontainers, conectado con @ServiceConnection. |
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.
Módulos
inferencellama al modelo y devuelve unProposal, un record que lleva el valor, el método y la procedencia. No depende derecordsni deaudit, y una verificación de Modulith o una regla de ArchUnit lo afirma en una prueba.recordses el único módulo que puede crear unVerifiedValue. El tipo no es público fuera del módulo, o es una clase cuyo constructor no lo es: un record público no puede ocultar su constructor, porque el constructor canónico de un record público tiene que ser público a su vez. La creación pasa por una factoría que exige la verificación de una persona con nombre.auditañade entradas con los campos publicados, dentro de la transacción del cambio que registra, a través de un repositorio que no ofrece actualización ni borrado.classificationdeclara el puerto;classification.vendorlo implementa y es el único módulo que puede ver el cliente del proveedor o sus tipos.policydeclara la interfaz de políticas y el registro de decisión; un módulo adaptador habla con el motor.
Fronteras en tiempo de ejecución
La validación ocurre en el binding web, donde Spring evalúa las restricciones sobre el cuerpo de la petición, y de nuevo dentro de la factoría del dominio, porque el binding web es un punto de entrada entre varios: un listener de mensajes, un job por lotes y una prueba llegan todos al dominio sin pasar por él.
Las observaciones envuelven la frontera y las llamadas externas. Cada executor que ejecuta trabajo para una petición se configura para propagar el contexto, y la identidad de auditoría es un campo de cada mensaje y de cada argumento de job, nunca algo que se recupera a posteriori de un thread-local.
Modos de fallo
La autoinvocación elude el proxy
Spring AOP se basa en proxies: una llamada de un método de un bean a otro método del mismo bean no pasa por el proxy, así que ni la validación de métodos ni
@Transactionalse le aplican. Una restricción que se cumple para quien llama desde fuera del bean no se cumple para el propio bean, que es a menudo donde empieza la ruta por lotes.Restricciones declaradas, nunca evaluadas
Las restricciones de Jakarta Validation sobre un tipo no hacen nada por sí solas. Se evalúan donde algo activa la validación:
@Valido@Validateden un binding web, o la validación de métodos a través del proxy. Un listener de mensajes que recibe el mismo tipo no valida nada salvo que lo pida.Propiedades no declaradas dejadas a la configuración del mapeador
Que un documento JSON entrante pueda llevar propiedades que el tipo no declara es un ajuste del mapeador JSON. Para un tipo de frontera, la respuesta correcta suele ser rechazarlas, y esa decisión corresponde al tipo y a una prueba, no a una propiedad de la aplicación que otra persona puede cambiar.
Contexto perdido en
@Asyncy en executors propiosPropagar el contexto de observación a otros hilos requiere un
ContextPropagatingTaskDecoratory la biblioteca context-propagation; para el executor autoconfigurado, Spring Boot lo deja como opción que se activa conspring.task.execution.propagate-context, desactivada por defecto. El trabajo entregado a un pool de hilos se ejecuta entonces sin su traza.La inmutabilidad del ORM tomada por una salvaguarda de auditoría
Hibernate ignora los cambios en memoria sobre una entidad gestionada
@Immutable, sin actualización y sin excepción. Las actualizaciones masivas contra ella lanzan una excepción por defecto en Hibernate 7 y en 6.6 solo generaban un aviso. Nada de esto impide que un segundo cliente, un script o una migración actualicen la tabla; la protección corresponde a la base de datos.Eventos de auditoría perdidos después del commit
Un evento que se procesa después del commit de la transacción se pierde si el handler falla, salvo que algo lo haya registrado. El registro de publicación de eventos de Spring Modulith escribe una entrada en la transacción que publica y conserva las publicaciones fallidas para volver a enviarlas; republicarlas automáticamente al reiniciar es un ajuste que hay que activar.
Un record público como tipo verificado
El constructor canónico de un record público tiene que ser público, así que cualquier código que pueda ver el tipo puede construir un valor verificado sin verificación. Hay que mantener el tipo fuera de su alcance, o usar una clase con constructor privado y una factoría.
Dos puentes de tracing
Micrometer Tracing tiende puentes hacia Brave o hacia OpenTelemetry, y su documentación pide elegir solo uno. Dos en el classpath producen trazas que dependen de cuál se imponga.
Pruebas de integración contra otra base de datos
Los permisos, los triggers y el comportamiento de las restricciones son propiedades de la base de datos real. Las pruebas contra un sustituto embebido verifican el código y no la propiedad.
Verificación
Prueba unitaria
Pasa cuando: El tipo verificado solo puede crearse a través de la factoría, y solo con la verificación de una persona con nombre; un rechazo deja el atributo vacío.
Prueba de dientes: Hacer público el tipo verificado en una rama desechable: falla una prueba que afirma que no es accesible desde el módulo de inferencia.
Prueba de arquitectura
Pasa cuando: La verificación de Spring Modulith pasa, o pasan las reglas de ArchUnit: ninguna dependencia de
inferencehaciarecordsoaudit, ningún tipo del proveedor en el dominio. ArchUnit comprueba las dependencias entre paquetes y clases como pruebas unitarias corrientes, y la verificación de Modulith rechaza los ciclos entre módulos y las referencias a los paquetes internos de otro módulo, salvo a módulos declarados abiertos. Cualquiera de las dos herramientas comprueba las reglas que se han escrito; que sean las reglas correctas depende del diseño del equipo.Prueba de dientes: Introducir a propósito la dependencia prohibida: la verificación falla.
Prueba de integración
Pasa cuando: Contra PostgreSQL desde Testcontainers: el rol de la aplicación no puede actualizar ni borrar la traza, y las entradas se escriben en la misma transacción que el cambio.
Prueba de dientes: Deshacer la transacción de negocio después de escribir: la entrada de auditoría no debe sobrevivir.
Prueba de integración
Pasa cuando: El contexto sobrevive a los executors que usa la aplicación: un exportador en memoria muestra el trabajo asíncrono como parte de la misma traza, y la identidad de auditoría figura en su registro.
Prueba de dientes: Quitar el task decorator: la prueba falla.
Prueba de contrato
Pasa cuando: Productor y consumidor coinciden en los contratos de propuesta y de decisión, con la procedencia y la revisión de la política como campos obligatorios.
Prueba de extremo a extremo
Pasa cuando: Una salida cuyo atributo no se ha verificado no puede publicarse, sea cual sea el punto de entrada por el que llegó la petición.
Realizaciones alternativas
- Otros frameworks Java (Quarkus, Micronaut, Jakarta EE) expresan la misma frontera con modelos de intercepción distintos; la pregunta de la autoinvocación hay que plantearla en cada uno.
- SQL explícito (jOOQ o JDBC directo) para la traza de auditoría en lugar de un ORM, donde las sentencias exactas importan más que la comodidad del mapeo.
- Frameworks de event sourcing donde el historial es el modelo.
- Tablas de reglas propiedad de la aplicación en lugar de un motor de políticas externo, con la misma exigencia de registrar la revisión de la política.
Compromisos
- Lo declarativo es conciso e invisible. Quien lee no puede ver desde el punto de llamada si se aplica una restricción o una transacción; tienen que mostrarlo las pruebas.
- Spring Modulith codifica convenciones, lo que lo hace rápido de adoptar y con opiniones propias; ArchUnit es general y exige escribir las reglas.
- JPA es cómodo y pone una capa entre el código y las protecciones propias de la base de datos. Para la traza de auditoría, esa capa es la parte a través de la cual hay que ver.
- Las fronteras de módulo dentro de un único desplegable mantienen sencillo el modelo operativo, y hacen que la frontera dependa por completo de la verificación del build.
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 Spring Boot 4.1, Spring Framework 7.0, Spring Modulith 2.1, Hibernate ORM 7.4 y Java SE 25.
- 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.
- Todo lo citado es comportamiento del lenguaje o del framework. La propiedad de arquitectura, que una propuesta no pueda convertirse en registro sin una verificación, es un diseño que el equipo aún tiene que hacer y probar: ninguna anotación, record ni regla de módulos la proporciona por sí sola.
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 Spring.
Entorno de referencia probado
No se ejecutó ningún ejemplo para este perfil.
Fuentes
- Oracle: java.lang.Record · documentación del lenguaje · Java SE 25 · Comprobado el 2026-09-11
- OpenJDK: JEP 409: Sealed Classes · especificación · Comprobado el 2026-09-11
- Oracle: Java Language Specification: canonical constructors of record classes · especificación · Java SE 25 · Comprobado el 2026-09-11
- Spring Framework: @RequestBody validation · documentación oficial · Spring Framework 7.0 · Comprobado el 2026-09-11
- Spring Framework: Understanding AOP proxies · documentación oficial · Spring Framework 7.0 · Comprobado el 2026-09-11
- Spring Framework: Spring-driven method validation · documentación oficial · Spring Framework 7.0 · Comprobado el 2026-09-11
- Spring Framework: Observability support: context propagation · documentación oficial · Spring Framework 7.0 · Comprobado el 2026-09-11
- Spring Boot: Observability: context propagation · documentación oficial · Spring Boot 4.1 · Comprobado el 2026-09-11
- Spring Boot: Tracing · documentación oficial · Spring Boot 4.1 · Comprobado el 2026-09-11
- Spring Boot: Testcontainers service connections · documentación oficial · Spring Boot 4.1 · Comprobado el 2026-09-11
- Spring Modulith: Verifying application module structure · documentación oficial · Spring Modulith 2.1 · Comprobado el 2026-09-11
- Spring Modulith: Event publication registry · documentación oficial · Spring Modulith 2.1 · Comprobado el 2026-09-11
- Hibernate ORM: Immutable entities · documentación oficial · Hibernate ORM 7.4 · Comprobado el 2026-09-11
- ArchUnit: ArchUnit user guide · documentación oficial · ArchUnit 1.5 · Comprobado el 2026-09-11
- Micrometer: Micrometer Tracing: supported tracers · documentación oficial · Micrometer Tracing 1.7 · Comprobado el 2026-09-11
