Dynamics Commons
1. Descripción general
dynamics-commons es una librería JAR compartida (Dynamics commons, según su pom.xml) que centraliza el modelo de acceso a datos utilizado por los microservicios de Hawkers que integran con Microsoft Dynamics 365 (ERP). Proporciona las entidades JPA, los repositorios Spring Data / JDBC y los servicios de acceso a base de datos PostgreSQL que evitan que cada microservicio consumidor duplique el modelo de datos y las queries contra las tablas relacionadas con Dynamics.
El proyecto no es un microservicio independiente: no expone endpoints REST, no tiene clase main ni application.properties/application.yml propios — toda la configuración de DataSource la aporta el servicio consumidor. Se distribuye como artefacto Maven versionado y se referencia como dependencia (com.hawkersco:dynamics-commons) desde runners y servicios como order-pi-dynamics, order-pi-dynamics-gold, products-dynamics-pi, return-pi-dynamics, etc.
Cubre los siguientes subdominios del ecosistema Dynamics 365:
- Pedidos (
OrderPending,OrderShipped,OrderStatus,DynamicsOrderPro,DynamicsOrderGold): ciclo de envío de pedidos a Dynamics, seguimiento de estado y reintentos de sincronización. - Inventario y productos (
ProductDynamics,VariantDynamics,InventDimensionsCombinations,KitVariantComponent): catálogo de productos, variantes y combinaciones de dimensiones de inventario (color/talla/estilo). - Productos publicados (
ReleasedProduct,CreativeDynamics,HWKInventItemBarcodes): productos liberados desde Dynamics y sus códigos de barra. - Precios (
SalesPriceAgreements,PricePrivalia): acuerdos de precio y precios específicos de canal Privalia. - Retail (
RetailStores,RetailAssortmentProductLines,RetailAssortmentChannelLines,RetailInformationSubcodes): catálogo de tiendas y surtido por canal/línea de producto. - Logística (
LogisticsOperatorInventLocations): ubicaciones de inventario por operador logístico (Auro, DHL, CLOSER, UPS). - Geografía (
AddressCountryRegion,CountyExternals): catálogos de país/región y códigos externos de condado. - Clientes (
PiCustomerSetup): configuración de clientes sincronizada desde Dynamics.
Dentro del ecosistema de microservicios de Hawkers, dynamics-commons actúa como capa de persistencia compartida: los runners que consumen eventos/webhooks de Dynamics 365 (vía el modelo GSON StatusOrdersDynamics) o que empujan datos hacia Dynamics usan esta librería para leer/escribir en las tablas PostgreSQL intermedias sin reimplementar entidades ni repositorios.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | dynamics-commons |
groupId | com.hawkersco |
version | 1.0.25-SNAPSHOT |
| Java | 25 |
| Spring Boot | 4.0.6 (Spring Framework 7.0.7) |
| Tipo de artefacto | JAR (librería, sin clase main) |
| Módulos | Proyecto mono-módulo |
| Repositorio Maven | Google Artifact Registry — europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven |
3. Arquitectura y diseño
Paquetes principales
com.hawkersco.dynamicscommons
├── dao/ # Entidades JPA (@Entity + Lombok), implementan Serializable
│ ├── pro/ # Variantes de tabla para el canal de negocio PRO
│ └── gold/ # Variantes de tabla para el canal de negocio GOLD
├── repository/ # Interfaces Spring Data (CrudRepository) + repositorios batch (JdbcTemplate)
│ ├── pro/
│ └── gold/
├── service/ # Servicios @Service, uno estándar y/o uno batch por entidad
│ ├── pro/
│ └── gold/
└── model/ # DTOs GSON (@SerializedName) para payloads/eventos de Dynamics
Patrón de capas
Cada dominio de datos sigue uno de estos dos patrones (a veces ambos coexisten para la misma tabla):
Patrón estándar — pensado para lecturas puntuales y actualizaciones de estado:
dao/EntidadX.java: entidad JPA con Lombok (@Getter,@Setter,@NoArgsConstructor,@AllArgsConstructor,@ToString),SerializableconserialVersionUIDexplícito.repository/EntidadXRepository.java: extiendeCrudRepository<EntidadX, ID>, con métodos derivados de Spring Data y@Query(JPQL onativeQuery = true) para consultas específicas. Las mutaciones masivas usan@Modifying+@Transactional.service/EntidadXService.java: inyección por campo@Autowired, delegación fina al repositorio.
Patrón batch — pensado para sincronizaciones masivas desde Dynamics (full-replace):
repository/EntidadXBatchRepository.java: clase@Repository(no interfaz) que usaJdbcTemplatedirectamente, contruncate()(TRUNCATE TABLE) ybatchInsert(List<EntidadX>)víaBatchPreparedStatementSetter.service/EntidadXBatchService.java: inyección por constructor, exponetruncateAndBatchInsert(List<EntidadX>)anotado con@Transactional, con guarda de lista nula/vacía.
Soporte multi-canal (PRO / GOLD)
Para las entidades de pedidos existen subpaquetes separados que replican la misma estructura pero contra tablas/namespaces distintos según el canal de negocio:
dao/pro/,repository/pro/,service/pro/— canal PRO (DynamicsOrderPro,OrderStatusPro).dao/gold/,repository/gold/,service/gold/— canal GOLD (DynamicsOrderGold,OrderStatusGold).
Las entidades compartidas (no ligadas a un canal) viven en los paquetes raíz; las variantes PRO/GOLD son estructuralmente idénticas mapeando a tablas distintas.
Flujo principal (sincronización batch desde Dynamics)
sequenceDiagram
participant D365 as Dynamics 365
participant Runner as Microservicio Runner
participant BatchSvc as XxxBatchService
participant BatchRepo as XxxBatchRepository
participant DB as PostgreSQL
D365->>Runner: Exporta datos (OData / export batch)
Runner->>Runner: Deserializa a List<EntidadX> (GSON, @SerializedName)
Runner->>BatchSvc: truncateAndBatchInsert(list)
BatchSvc->>BatchRepo: truncate()
BatchRepo->>DB: TRUNCATE TABLE entidad_x
BatchSvc->>BatchRepo: batchInsert(list)
BatchRepo->>DB: INSERT batch (JdbcTemplate.batchUpdate)
Flujo principal (pedidos pendientes de envío a Dynamics)
sequenceDiagram
participant Runner as Microservicio Runner
participant Svc as OrderPendingService
participant Repo as OrderPendingRepository
participant DB as PostgreSQL
participant D365 as Dynamics 365
Runner->>Svc: findByIsSendDynamicsFalse()
Svc->>Repo: query nativa (is_send_dynamics = false AND nm_send_dynamics <= 4)
Repo->>DB: SELECT
DB-->>Runner: List<OrderPending>
Runner->>D365: Envía pedido
alt Envío OK
Runner->>Svc: updateIsSendDynamics(true, dsOrder)
else Envío falla
Runner->>Svc: updateNmSendDynamics(n+1, dsOrder)
end
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
org.springframework.boot:spring-boot-starter | Núcleo de Spring Boot (contexto, inyección de dependencias). |
org.springframework.boot:spring-boot-starter-data-jpa | Spring Data JPA + Hibernate para las entidades y repositorios CrudRepository. |
org.postgresql:postgresql | Driver JDBC de PostgreSQL (versión gestionada por el parent de Spring Boot). |
org.projectlombok:lombok | Generación de getters/setters/constructores en entidades y DTOs. |
com.google.code.gson:gson | Serialización/deserialización de los DTOs de eventos de Dynamics (@SerializedName). |
org.springframework.boot:spring-boot-starter-test (test) | Soporte de test de Spring Boot (sin tests implementados actualmente). |
Extensión de build relevante: com.google.cloud.artifactregistry:artifactregistry-maven-wagon (2.2.1) — necesaria para publicar/resolver contra el Google Artifact Registry corporativo.
5. API / Endpoints
No aplica. dynamics-commons es una librería de acceso a datos sin capa web ni controladores REST; no expone endpoints propios.
6. Integraciones externas
| Sistema | Protocolo / Mecanismo | Dirección del flujo |
|---|---|---|
| Microsoft Dynamics 365 | Indirecta: la librería no llama a Dynamics directamente. Modela los payloads de eventos/webhooks (StatusOrdersDynamics, DTOs @SerializedName en dao/) y persiste datos exportados/importados por los servicios consumidores. | Entrante (datos que llegan desde Dynamics) y saliente (pedidos/estados que los runners consumidores envían a Dynamics usando estas entidades). |
| PostgreSQL | JDBC / Spring Data JPA + JdbcTemplate | Bidireccional (lectura y escritura). |
Microservicios Hawkers consumidores (order-pi-dynamics, order-pi-dynamics-gold, products-dynamics-pi, return-pi-dynamics, etc.) | Dependencia Maven (com.hawkersco:dynamics-commons) | Saliente (esta librería es consumida como dependencia de código, no se comunica en runtime con ellos). |
| Operadores logísticos (Auro, DHL, CLOSER, UPS) | Modelados como datos en LogisticsOperatorInventLocations (no hay integración de red directa desde esta librería). | N/A (solo modelo de datos). |
7. Configuración
No aplica. El proyecto es una librería y no incluye application.properties ni application.yml; toda la configuración de conexión a base de datos (DataSource, credenciales, URL JDBC) la define el microservicio consumidor en su propia configuración.
No se han encontrado variables de entorno propias del proyecto.
8. Persistencia
Base de datos: PostgreSQL. Las entidades mapean a las siguientes tablas principales (nombre de tabla vía @Table):
| Entidad | Tabla | Notas |
|---|---|---|
OrderPending | order_pending | Cola de pedidos pendientes de envío a Dynamics, con contador de reintentos. |
OrderShipped | order_shipped | Registro de pedidos ya enviados, incluye raw_data y raw_response. |
OrderStatus | order_status | Estado de pedido con operador logístico asociado; implementa Comparable por dtSysCreated. |
DynamicsOrderPro / DynamicsOrderGold | dynamics_order (por canal) | Cabecera de pedido Dynamics por canal PRO/GOLD, PK de tipo String. |
ReleasedProduct | released_product | Productos liberados desde Dynamics (OData), incluye odata_etag. |
ProductDynamics / VariantDynamics | tablas de producto/variante | Catálogo e inventario de variantes. |
InventDimensionsCombinations | combinaciones de dimensión | Búsqueda por item + config/estilo/color+talla. |
SalesPriceAgreements / PricePrivalia | acuerdos de precio | Precios por canal. |
RetailStores, RetailAssortmentProductLines, RetailAssortmentChannelLines, RetailInformationSubcodes | catálogos retail | Surtido y tiendas. |
LogisticsOperatorInventLocations | logistics_operator_invent_locations | Ubicaciones de inventario por operador logístico. |
AddressCountryRegion, CountyExternals | catálogos geográficos | Región/país y condados externos. |
PiCustomerSetup | configuración de cliente | Setup de clientes sincronizado desde Dynamics. |
No se ha encontrado herramienta de migraciones (Flyway/Liquibase) en el proyecto: la evolución del esquema de las tablas es responsabilidad externa a esta librería (Pendiente de verificar dónde se gestiona).
Las operaciones batch (XxxBatchRepository.truncate() + batchInsert()) implementan semántica de reemplazo completo (truncate-then-insert), no upsert — cada sincronización sustituye por completo el contenido de la tabla afectada.
9. Procesos programados y mensajería
No aplica. No se han encontrado @Scheduled, @KafkaListener, @RabbitListener ni runners batch ejecutables dentro de este proyecto: los procesos que orquestan la sincronización periódica y consumen colas/eventos residen en los microservicios consumidores (runners), que invocan los servicios de esta librería.
10. Ejecución en local
Como librería, no se "ejecuta" de forma independiente. Para desarrollarla o consumirla en local:
Requisitos previos: JDK 25, Maven (o el wrapper ./mvnw incluido).
Build e instalación en repositorio Maven local:
mvn -B -DskipTests clean install
Limpieza de artefactos de build:
rm -rf ./target/*
No existen tests unitarios (src/test/java no existe), por lo que no hay comando de test específico. No aplica verificación vía Actuator/health, al no ser un servicio desplegable.
Los microservicios que dependen de esta librería deben declarar com.hawkersco:dynamics-commons:<version> en su pom.xml; tras cambios locales, publicar primero con mvn -B -DskipTests clean install para que quede disponible en ~/.m2.
11. Despliegue
El despliegue consiste en la publicación del artefacto Maven al Google Artifact Registry corporativo (no hay despliegue de contenedor/servicio, al ser una librería).
Pipeline Jenkins (Jenkinsfile, agente any, JDK25 + Maven3):
- Checkout del repositorio.
- Publish to Artifact Registry:
mvn deploy -DskipTests, que publica enartifactregistry://europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven(definido endistributionManagementdelpom.xml).
Job de Jenkins:
https://jenkins-pi.hawkersco.net/job/dynamics-commons/
Los consumidores deben actualizar la versión de la dependencia en su propio pom.xml y redesplegar tras cada publicación relevante.
12. Manejo de errores y logging
No se ha encontrado estrategia de excepciones propia (no hay @ControllerAdvice, excepciones custom ni códigos de error definidos en el proyecto): al ser una librería de acceso a datos, las excepciones de persistencia (DataAccessException, EmptyResultDataAccessException, etc.) se propagan sin envolver al código consumidor, que es responsable de su tratamiento.
No se ha encontrado configuración de logging propia (no hay logback.xml/log4j2.xml ni niveles de log configurados); el logging queda delegado a la configuración del microservicio que integra la librería.
13. Notas y consideraciones
- Doble patrón repositorio/servicio por entidad: varias entidades tienen tanto una versión "estándar" (
CrudRepository) como una versión "batch" (JdbcTemplate) — por ejemplo,ReleasedProductsolo tiene el patrón batch, mientras queOrderPendingsolo tiene el patrón estándar. Al añadir nuevas entidades, seguir el patrón ya usado por entidades del mismo dominio para mantener la coherencia. - Java 25 sin autodetección de Lombok: el
maven-compiler-pluginrequiere declarar explícitamenteannotationProcessorPathspara Lombok 1.18.46; si se omite, las entidades no compilan pese a la anotación@Entity/@Getter/@Setter. - Semántica full-replace en operaciones batch: los métodos
truncateAndBatchInsert()truncan la tabla antes de insertar. Cualquier consumidor que dependa de conservar histórico en estas tablas debe extraer los datos antes de invocar la sincronización batch. OrderPendingRepository.findByIsSendDynamicsFalse()limita los reintentos anm_send_dynamics <= 4(máximo 5 intentos) directamente en la query SQL nativa — cambiar este límite requiere modificar la query, no es configurable externamente.- Ausencia de tests: no existe
src/test/java, tal y como confirmaCLAUDE.md; cualquier cambio en entidades/repositorios se valida únicamente al compilar/consumir desde un microservicio real. ReleasedProduct.isEquals(...): método de comparación de igualdad de negocio (no sobreescribeequals()) que no comprueba nulls en los campos comparados — puede lanzarNullPointerExceptionsiitemNumber,salesUnitSymbol,salesSalesTaxItemGroupCodeodataAreaIdsonnullen cualquiera de las dos instancias comparadas.