🗺️ Mapeamento de Objetos com MapStruct

⚠️

Acesso Antecipado (Beta)

Esta documentação refere-se a uma versão em acesso antecipado do SDK Sankhya. As funcionalidades e APIs estão sujeitas a modificações. Para obter acesso, envie um e-mail para [email protected] informando a appkey do seu projeto.

🗺️ Mapeamento de Objetos com MapStruct

O SDK Sankhya oferece integração nativa com o MapStruct, uma biblioteca poderosa que simplifica o mapeamento entre diferentes camadas de objetos (como DTOs, Entidades JAPE e outros beans).

Com o MapStruct, você define uma interface de mapeamento e a biblioteca gera a implementação automaticamente em tempo de compilação, eliminando a necessidade de escrever código de conversão manual, repetitivo e propenso a erros.

🤔 Por que usar MapStruct?

  • Produtividade Máxima: Automatiza a criação de código de mapeamento.
  • Performance: O código gerado é tão rápido quanto o mapeamento manual, sem usar reflection.
  • Segurança de Tipos: Erros de mapeamento são detectados em tempo de compilação.
  • Código Limpo: Suas classes de negócio ficam livres da lógica de conversão.

⚙️ Como Funciona: Detecção Automática

O processador de anotações do Sankhya Studio é inteligente. Ele detecta automaticamente se o MapStruct está presente no classpath do seu projeto.

  • Se o MapStruct for encontrado, o SDK ativa a integração, processa suas interfaces anotadas com @Mapper e as registra no contêiner de injeção de dependências.
  • Se não for encontrado, a funcionalidade é simplesmente ignorada, sem causar erros.

Durante a compilação, você verá uma das seguintes mensagens no log:

✅ MapStruct detectado no classpath. MapperProcessor será ativado.

ou

ℹ️ MapStruct não detectado no classpath. MapperProcessor será ignorado.

🛠️ Configuração do Projeto

Para usar o MapStruct, você precisa adicionar as dependências ao build.gradle do seu addon.

1. Dependências para Projetos Java

Se seu projeto é 100% Java, adicione as seguintes dependências:

// build.gradle

dependencies {
    // API do MapStruct
    implementation 'org.mapstruct:mapstruct:1.5.5.Final'
    
    // Processador de anotações do MapStruct
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}

// Opcional: configure o compilador para passar opções ao MapStruct
tasks.withType(JavaCompile) {
    options.compilerArgs += [
        '-Amapstruct.defaultComponentModel=cdi',
        '-Amapstruct.unmappedTargetPolicy=IGNORE'
    ]
}

2. Dependências para Projetos Kotlin (com KSP)

Se seu projeto utiliza Kotlin, a configuração é feita com o KSP (Kotlin Symbol Processing).

// build.gradle

plugins {
    id 'java'
    id 'com.google.devtools.ksp' version '2.0.0-1.0.21' // Verifique a versão compatível
}

dependencies {
    // API do MapStruct
    implementation 'org.mapstruct:mapstruct:1.5.5.Final'
    
    // Processador de anotações do MapStruct para KSP
    ksp 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}

// Adicione o diretório gerado pelo KSP às fontes do Kotlin
kotlin {
    sourceSets {
        main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin'
    }
}
DependênciaVersão Recomendada
org.mapstruct1.5.5.Final
com.google.devtools.ksp2.0.0-1.0.21

✨ Exemplo Prático de Uso

Vamos ver como criar, injetar e usar um mapper em um serviço.

Passo 1: Crie a Interface do Mapper

Defina uma interface e anote-a com @Mapper. O SDK recomenda o uso do componentModel = "cdi" para melhor integração com o ecossistema de injeção de dependências.

// br/com/fabricante/addon/mapper/UserMapper.java

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "cdi")
public interface UserMapper {

    // Opcional: permite obter uma instância se a DI não estiver disponível
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "nomeUsuario", target = "username")
    @Mapping(source = "ativo", target = "enabled")
    UserDTO toDTO(UserJapeEntity entity);

    @Mapping(target = "nomeUsuario", source = "username")
    @Mapping(target = "ativo", source = "enabled")
    UserJapeEntity toEntity(UserDTO dto);
    
    List<UserDTO> toDTOList(List<UserJapeEntity> entities);
}

Passo 2: Injete o Mapper em um Componente

Graças à integração automática, você pode injetar seu mapper diretamente em qualquer classe gerenciada pelo SDK (@Component, @Service, etc.).

// br/com/fabricante/addon/service/UserService.java

@Component
public class UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    @Inject
    public UserService(UserRepository userRepository, UserMapper userMapper) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
    }

    @Transactional
    public UserDTO findUserById(Long userId) {
        UserJapeEntity userEntity = userRepository.findById(userId)
            .orElseThrow(() -> new NotFoundException("Usuário não encontrado"));
        
        // Use o mapper para converter a entidade em DTO
        return userMapper.toDTO(userEntity);
    }
    
    public void createUser(UserDTO userDTO) {
        // Use o mapper para converter o DTO em entidade antes de salvar
        UserJapeEntity userEntity = userMapper.toEntity(userDTO);
        userRepository.save(userEntity);
    }
}

O código gerado pelo MapStruct será automaticamente registrado no contêiner de DI, e o UserService receberá a instância correta sem nenhuma configuração adicional.


🚨 Troubleshooting (Solução de Problemas)

Problema: Meu mapper não está sendo injetado (UnsatisfiedDependencyException).

Causas e Soluções:

  1. Dependências Incorretas:

    • Verifique se mapstruct-processor está na configuração annotationProcessor (para Java) ou ksp (para Kotlin). A dependência mapstruct (API) deve estar em implementation.
  2. componentModel Incorreto:

    • Certifique-se de que sua anotação @Mapper tenha componentModel = "cdi" ou componentModel = "default". Isso instrui o MapStruct a gerar um bean que o SDK pode detectar.
  3. Projeto Não Recompilado:

    • Às vezes, o ambiente de desenvolvimento pode não acionar o processador de anotações. Force uma recompilação limpa:
      ./gradlew clean build
      

Problema: O processador do MapStruct parece não rodar.

Causas e Soluções:

  1. Conflito de Processadores:

    • Em projetos complexos, a ordem dos processadores de anotação pode importar. Verifique se não há configurações conflitantes no seu build.gradle.
  2. Cache do Gradle:

    • Limpe o cache do Gradle para garantir que nenhuma dependência antiga esteja sendo usada:
      ./gradlew clean --refresh-dependencies
      

🔗 Referências