Skip to main content

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

PropiedadValor
artifactIddynamics-commons
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6 (Spring Framework 7.0.7)
Tipo de artefactoJAR (librería, sin clase main)
MódulosProyecto mono-módulo
Repositorio MavenGoogle 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), Serializable con serialVersionUID explícito.
  • repository/EntidadXRepository.java: extiende CrudRepository<EntidadX, ID>, con métodos derivados de Spring Data y @Query (JPQL o nativeQuery = 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 usa JdbcTemplate directamente, con truncate() (TRUNCATE TABLE) y batchInsert(List<EntidadX>) vía BatchPreparedStatementSetter.
  • service/EntidadXBatchService.java: inyección por constructor, expone truncateAndBatchInsert(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

DependenciaPropósito
org.springframework.boot:spring-boot-starterNúcleo de Spring Boot (contexto, inyección de dependencias).
org.springframework.boot:spring-boot-starter-data-jpaSpring Data JPA + Hibernate para las entidades y repositorios CrudRepository.
org.postgresql:postgresqlDriver JDBC de PostgreSQL (versión gestionada por el parent de Spring Boot).
org.projectlombok:lombokGeneración de getters/setters/constructores en entidades y DTOs.
com.google.code.gson:gsonSerializació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

SistemaProtocolo / MecanismoDirección del flujo
Microsoft Dynamics 365Indirecta: 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).
PostgreSQLJDBC / Spring Data JPA + JdbcTemplateBidireccional (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):

EntidadTablaNotas
OrderPendingorder_pendingCola de pedidos pendientes de envío a Dynamics, con contador de reintentos.
OrderShippedorder_shippedRegistro de pedidos ya enviados, incluye raw_data y raw_response.
OrderStatusorder_statusEstado de pedido con operador logístico asociado; implementa Comparable por dtSysCreated.
DynamicsOrderPro / DynamicsOrderGolddynamics_order (por canal)Cabecera de pedido Dynamics por canal PRO/GOLD, PK de tipo String.
ReleasedProductreleased_productProductos liberados desde Dynamics (OData), incluye odata_etag.
ProductDynamics / VariantDynamicstablas de producto/varianteCatálogo e inventario de variantes.
InventDimensionsCombinationscombinaciones de dimensiónBúsqueda por item + config/estilo/color+talla.
SalesPriceAgreements / PricePrivaliaacuerdos de precioPrecios por canal.
RetailStores, RetailAssortmentProductLines, RetailAssortmentChannelLines, RetailInformationSubcodescatálogos retailSurtido y tiendas.
LogisticsOperatorInventLocationslogistics_operator_invent_locationsUbicaciones de inventario por operador logístico.
AddressCountryRegion, CountyExternalscatálogos geográficosRegión/país y condados externos.
PiCustomerSetupconfiguración de clienteSetup 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):

  1. Checkout del repositorio.
  2. Publish to Artifact Registry: mvn deploy -DskipTests, que publica en artifactregistry://europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven (definido en distributionManagement del pom.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, ReleasedProduct solo tiene el patrón batch, mientras que OrderPending solo 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-plugin requiere declarar explícitamente annotationProcessorPaths para 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 a nm_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 confirma CLAUDE.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 sobreescribe equals()) que no comprueba nulls en los campos comparados — puede lanzar NullPointerException si itemNumber, salesUnitSymbol, salesSalesTaxItemGroupCode o dataAreaId son null en cualquiera de las dos instancias comparadas.