Install
$ agentstack add skill-snk-devcenter-addon-studio-mapstruct ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
MapStruct — Addon Studio 2.0
MapStruct = biblioteca padrao pra conversao DTO entidade. Nunca faca mapper manual — use MapStruct.
> "Dominio", nesta skill e nas demais do plugin, e a entidade @JapeEntity. Os metodos toDomain/fromDomain convertem DTO entidade — nao existe um terceiro modelo entre eles. Criar POJO de dominio separado da entidade e decisao explicita do projeto (ver skill controller, "O que atravessa controller -> service"), nunca inferida a partir do nome dos metodos.
> Referencia complementar: veja dependency-injection pra como mappers registram no container Guice. > > Escopo: arquivo define so regras genericas MapStruct. Regras negocio, filtros plataforma, convencoes dominio especificas ficam em override por projeto (fora desta skill).
1. Configuracao Global do Projeto
Projeto ja define flags globais compilacao no build.gradle:
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
}
tasks.withType(JavaCompile) {
options.compilerArgs += [
"-Amapstruct.defaultComponentModel=jakarta",
"-Amapstruct.unmappedTargetPolicy=IGNORE"
]
}
O que cada flag faz
| Flag | Valor | Efeito | |:-----|:------|:-------| | defaultComponentModel | jakarta | MapStruct gera implementacoes com @Named pra registro automatico Guice. NAO sobrescrever nos mappers. | | unmappedTargetPolicy | IGNORE | Campos target sem mapeamento explicito ignorados sem erro compilacao. | | lombok-mapstruct-binding | 0.2.0 | Garante compatibilidade Lombok (getters/setters gerados) com MapStruct (annotation processor). |
> ATENCAO — Nao confunda componentModel com injectionStrategy: > > | Parametro | Configurado globalmente? | Regra | > |:----------|:-------------------------|:------| > | componentModel | SIM — jakarta no build.gradle | NUNCA declare no @Mapper individual. Declarar sobrescreve global. | > | injectionStrategy | NAO — sem flag global | SEMPRE declare InjectionStrategy.CONSTRUCTOR em mapper abstract class (com @Inject repository) ou que use uses = {...}. | > | repositories em abstract class | N/A | SEMPRE use field injection (@Inject no campo). Limitacao MapStruct: processador nao gera super(...) com parametros construtor classe abstrata, impossibilita constructor injection pra repositorios. | > > IMPORTANTE — componentModel: componentModel NUNCA declare no @Mapper individual. Ja global como jakarta em build.gradle. Declarar — mesmo com mesmo valor "jakarta" — sobrescreve global, causa conflitos injecao ou falhas silenciosas no Guice. > > IMPORTANTE — injectionStrategy: injectionStrategy NAO global. Declare explicito como InjectionStrategy.CONSTRUCTOR em mapper que: > - abstract class com @Inject (repositorios ou deps), OU > - use uses = {...} com componentes externos. > > Omitir = field injection Guice sem garantia ordem inicializacao.
2. Tipos de Mapper
4 configuracoes mapper no projeto, por contexto:
| Tipo | @Mapper(...) | Classe | Quando usar | |:-----|:---------------|:-------|:------------| | Simples | @Mapper | interface | Mappers sem deps externas | | Com uses | @Mapper(uses = {...}, injectionStrategy = CONSTRUCTOR) | interface | Mappers que usam classes auxiliares @Component | | Com uses + builder desabilitado | @Mapper(uses = {...}, injectionStrategy = CONSTRUCTOR, builder = @Builder(disableBuilder = true)) | abstract class | Mappers com @AfterMapping ou logica custom que exige setters | | Com Repository (create/merge) | @Mapper(uses = {...}, injectionStrategy = CONSTRUCTOR, builder = @Builder(disableBuilder = true)) | abstract class + @Inject field (repository) | Mappers integracao com upsert: busca por chave externa/negocial, atualiza se existe ou cria se nao existe |
3. Mapper Simples (interface)
Conversoes diretas sem deps externas.
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface MeuRestMapper {
MeuEntity toDomain(MeuRequest dto);
@Mapping(source = "campo1", target = "campoDto1")
@Mapping(source = "campo2", target = "campoDto2")
MeuResponse toResponse(MeuEntity domain);
}
Caracteristicas:
interface.- So
@Mapper(sem params). - Injetavel direto via
@Injectconstrutor.
4. Mapper com uses (interface + @Component)
Mapper precisa classe auxiliar pra transformacoes custom (ex: normalizar strings, parsear datas).
Classe auxiliar (@Component)
import br.com.sankhya.studio.stereotypes.Component;
@Component
public class StringMappingNormalizer {
public String normalize(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
}
Mapper usando classe auxiliar
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(
uses = {StringMappingNormalizer.class},
injectionStrategy = InjectionStrategy.CONSTRUCTOR
)
public interface MeuIntegrationMapper {
@Mapping(source = "idExterno", target = "idOrigem")
@Mapping(source = "nome", target = "descricao")
@Mapping(target = "ativo", constant = "true")
MeuEntity toDomain(MeuExternalDTO dto);
}
Regras obrigatorias:
- Classe em
usesdeve ser@Component(pra Guice resolver). injectionStrategy = InjectionStrategy.CONSTRUCTORobrigatorio comuses.- MapStruct aplica metodos auto quando tipos entrada/saida batem (ex:
String -> Stringusanormalize).
5. Mapper com Builder Desabilitado (abstract class)
Mapper precisa @AfterMapping, logica custom em metodos concretos, ou target usa Builder e voce precisa setters.
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(
uses = {StringMappingNormalizer.class},
injectionStrategy = org.mapstruct.InjectionStrategy.CONSTRUCTOR,
builder = @org.mapstruct.Builder(disableBuilder = true)
)
public abstract class MeuMapper {
@Mapping(source = "idExterno", target = "cultura.idOrigem")
@Mapping(source = "idAlvo", target = "alvo.idOrigem")
@Mapping(source = "doseMin", target = "doseMinima")
public abstract MeuEntity toDomain(MeuExternalDTO dto);
}
Quando usar:
- Target tem
@Builder(Lombok) e MapStruct deve usar setters em vez do Builder. - Precisa
@AfterMappingpra pos-processamento. - Classe tem que ser
abstract class(naointerface) pra ter metodos concretos.
6. Padroes de Mapeamento
Padroes de mapeamento — campos com nomes diferentes, nested (objetos aninhados), ignore explicito, valores constantes, mapeamento bidirecional, mapeamento implicito por mesmo nome, e padrao create/merge (upsert) com Repository — em [references/patterns.md](references/patterns.md).
7. Organizacao
> Skill nao opina sobre pacotes. Padrao comum: > > - REST mappers: junto do Controller (DTO Request/Response ↔ entidade @JapeEntity). > - Integration mappers: junto do client concreto da plataforma externa (DTO da API ↔ entidade @JapeEntity). > - Classes auxiliares compartilhadas (@Component usados via uses = {...}): em local de utilidades compartilhadas.
Convencao de nome
| Tipo de Mapper | Convencao de nome | Exemplo | |:------------------------------|:-----------------------------------|:-------------------------| | REST (Controller) | RestMapper | PedidoRestMapper | | Integracao externa | Mapper | PlataformaXProdutoMapper | | Classe auxiliar compartilhada | Nome descritivo | StringMappingNormalizer |
8. Convencao de Metodos
| Direcao | Nome do metodo | Assinatura | |:--------|:---------------|:-----------| | DTO externo -> entidade | toDomain | Entity toDomain(ExternalDTO dto) | | Entidade -> DTO externo | fromDomain | ExternalDTO fromDomain(Entity domain) | | Request -> entidade | to | Entity to(Request dto) | | Entidade -> Response | to | Response to(Entity domain) |
Exemplos
// Integracao: DTO externo Dominio
Produto toDomain(PlataformaXProduto dto);
PlataformaXProduto fromDomain(Produto domain);
// REST: Request -> Dominio, Dominio -> Response
Pedido toPedido(PedidoRequest dto);
EmitirPedidoResponse toEmitirPedidoResponse(Pedido pedido);
9. Injecao e Uso
Mappers injetam via @Inject no construtor como qualquer dep Guice. Exemplos completos de uso em controller e gateway de integracao em [references/injection-usage.md](references/injection-usage.md).
10. Fluxo de Decisao: Qual tipo de Mapper usar?
Mapper de integracao com plataforma externa (toDomain precisa de upsert)?
|
+-- SIM --> abstract class + @Inject repository + toDomain concreto com create/merge
| @Mapper(uses={...}, injectionStrategy=CONSTRUCTOR, builder=@Builder(disableBuilder=true))
|
+-- NAO --> Precisa de classe auxiliar em `uses`?
|
+-- NAO --> @Mapper (interface simples)
|
+-- SIM --> Precisa de @AfterMapping ou logica customizada?
|
+-- NAO --> @Mapper(uses={...}, injectionStrategy=CONSTRUCTOR) [interface]
|
+-- SIM --> @Mapper(uses={...}, injectionStrategy=CONSTRUCTOR,
builder=@Builder(disableBuilder=true)) [abstract class]
11. Checklist
Novo mapper
- Identificar tipo correto (simples, com
uses, builder desabilitado, ou create/merge com repository). - Mapper integracao com plataforma externa:
abstract classcom@Injectrepository. - Declarar
interface(ouabstract classse precisar builder desabilitado ou repository). - NAO declarar
componentModel— ja global comojakartaembuild.gradle. Nem com valor"jakarta". - Se
abstract class(com@Injectrepository) OU usaruses, declareinjectionStrategy = InjectionStrategy.CONSTRUCTOR. - Se usar
uses, garanta classes referenciadas sao@Component. - Mapear campos nomes diferentes via
@Mapping(source, target). - Usar
target = "nested.field"pra objetos aninhados. - Usar
ignore = truepra campos nao mapeaveis. - Usar
constant = "valor"pra valores fixos. - Pacote conforme arquitetura do projeto (skill nao opina).
- Injetar via construtor com
@Inject.
Mapper de integracao (create/merge)
- Declarar
abstract class. - Adicionar
injectionStrategy = InjectionStrategy.CONSTRUCTORno@Mapper— obrigatorio praabstract classcom@Inject. - Adicionar
builder = @Builder(disableBuilder = true)no@Mapper. - Injetar repository com
@Injectcomo campo protegido — field injection obrigatoria por limitacao MapStruct (ver nota "Por que field injection" em [references/patterns.md](references/patterns.md)). - Implementar
toDomain()concreto:findBy()→doUpdatese existe,doMapse nao. - Declarar
doMap(DTO)protected abstractcom@Mapping(target="", ignore=true). - Declarar
doUpdate(@MappingTarget Entity, DTO)protected abstractcom@Mapping(target="", ignore=true). - No
doUpdate, ignorar campos dominio que origem externa nao deve sobrescrever. - Direcao inversa (export): declare
fromDomain(Entity)public abstract.
Revisao de mapper existente
- Verificar se campos source estao mapeados no target (ou ignorados explicito).
- Verificar campos nested (
a.b.c) corretos. - Verificar
usesteminjectionStrategy = CONSTRUCTOR. Seabstract classcom@Injectrepository, tambem teminjectionStrategy = CONSTRUCTOR. - Verificar que NAO tem
componentModelno@Mapper(nem"jakarta"— ja global). - Mapper integracao: verificar se implementa create/merge com repository.
12. Erros Comuns
| Erro | Correcao | |:-----|:---------| | Declarar componentModel no @Mapper (qualquer valor, incluindo "jakarta") | Remover — ja global como jakarta em build.gradle. Declarar sobrescreve global. | | Usar uses sem injectionStrategy = CONSTRUCTOR | Adicionar injectionStrategy = InjectionStrategy.CONSTRUCTOR. | | abstract class com @Inject repository sem injectionStrategy = CONSTRUCTOR | Adicionar injectionStrategy = InjectionStrategy.CONSTRUCTOR — nao e global. | | Constructor injection (@Inject no construtor da abstract class) pra repositorios | Reverter pra field injection (@Inject no campo). MapStruct nao gera super(...) com params do construtor da abstrata; classe gerada nao instancia. | | Classe em uses sem @Component | Adicionar @Component na auxiliar. | | @AfterMapping nao executado | Adicionar builder = @Builder(disableBuilder = true) no @Mapper e usar abstract class. | | Mapper abstract class sem builder = @Builder(disableBuilder = true) | Adicionar atributo — sem ele MapStruct pode usar Builder Lombok e ignorar setters. | | Mapper manual (new MeuDTO(); dto.setX(...)) | Usar MapStruct. Nunca mapper manual. | | Campo target nao populado | Verificar se @Mapping correto. Mesmo nome = automatico; diferentes precisam @Mapping explicito. | | Erro compilacao "Ambiguous mapping methods" | Renomear metodos pra evitar conflitos assinatura ou usar @Named pra qualificar. | | Erro compilacao com Lombok | Verificar lombok-mapstruct-binding nas deps do annotationProcessor. |
Skills relacionadas
dependency-injection— mappers registram no container Guicecontroller— controllers consomem mappers para conversão DTO ↔ entidadeentity— entidades origem/destino dos mapeamentos
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: snk-devcenter
- Source: snk-devcenter/addon-studio
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.