Skip to content
intermediatePhase ·

Mapper

Learn to map between entities and DTOs cleanly.

30m
0 problems
Topic Progress0%

Entity-DTO Mapping

Mappers convert between entities and DTOs. This keeps your layers decoupled.

Manual Mapper (Simple and Explicit)

@Component
public class ProductMapper {

    public ProductDto toDto(Product product) {
        ProductDto dto = new ProductDto();
        dto.setId(product.getId());
        dto.setName(product.getName());
        dto.setDescription(product.getDescription());
        dto.setPrice(product.getPrice());
        dto.setCategoryName(product.getCategory().getName());
        dto.setStockQuantity(product.getStockQuantity());
        dto.setActive(product.getActive());
        dto.setCreatedAt(product.getCreatedAt());
        return dto;
    }

    public Product toEntity(CreateProductRequest request) {
        Product product = new Product();
        product.setName(request.getName());
        product.setDescription(request.getDescription());
        product.setPrice(request.getPrice());
        product.setStockQuantity(request.getStockQuantity());
        return product;
    }

    public void updateEntity(UpdateProductRequest request, Product product) {
        product.setName(request.getName());
        product.setDescription(request.getDescription());
        product.setPrice(request.getPrice());
    }
}

Usage in Service

@Service
public class ProductService {

    private final ProductRepository productRepository;
    private final ProductMapper productMapper;

    public ProductService(ProductRepository productRepository, ProductMapper productMapper) {
        this.productRepository = productRepository;
        this.productMapper = productMapper;
    }

    public ProductDto getProduct(Long id) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
        return productMapper.toDto(product);
    }

    @Transactional
    public ProductDto createProduct(CreateProductRequest request) {
        Product product = productMapper.toEntity(request);
        // Set relationships
        Category category = categoryRepository.findById(request.getCategoryId())
            .orElseThrow(() -> new ResourceNotFoundException("Category not found"));
        product.setCategory(category);

        Product saved = productRepository.save(product);
        return productMapper.toDto(saved);
    }
}

MapStruct (Compile-Time Code Generation)

@Mapper(componentModel = "spring")
public interface ProductMapper {

    @Mapping(source = "category.name", target = "categoryName")
    @Mapping(target = "createdAt", ignore = true)
    ProductDto toDto(Product product);

    @Mapping(target = "id", ignore = true)
    @Mapping(target = "category", ignore = true)
    Product toEntity(CreateProductRequest request);

    @Mapping(target = "id", ignore = true)
    @Mapping(target = "category", ignore = true)
    void updateEntity(UpdateProductRequest request, @MappingTarget Product product);
}

MapStruct generates the mapping code at compile time — no reflection overhead.

When to Use Each Approach

Approach Pros Cons
Manual Full control, easy to debug Verbose, error-prone for many fields
MapStruct Fast, type-safe, compile-time Extra dependency, learning curve
ModelMapper Convention-based, minimal code Hard to debug, reflection overhead

Best Practices

Key Principles

  1. Follow SOLID principles
  2. Write clean, readable code
  3. Test thoroughly
  4. Document decisions
  5. Monitor in production

Implementation

  • Start simple, refactor as needed
  • Use established patterns
  • Consider trade-offs
  • Review with peers

Continuous Improvement

  • Learn from incidents
  • Update documentation
  • Share knowledge
  • Mentor others

Key Points

  • Understanding Mapper is essential for production systems
  • Always consider scalability and maintainability
  • Test thoroughly before deploying to production
  • Monitor performance and set up alerting

Common Patterns

  1. Validation: Always validate input at the boundary
  2. Error Handling: Use structured error responses
  3. Logging: Log key events for debugging
  4. Testing: Unit, integration, and load tests
  5. Documentation: Keep docs updated with code changes

Practice Problems

0/3solved
Implement Mapper

Design and implement a solution for Mapper in a backend system. Consider scalability, error handling, and production readiness.

Solution
// Mapper implementation
// Key aspects: validation, error handling, logging, testing

public class Mapper {
    // Production-ready implementation
}
Mapper Edge Cases

Identify and handle edge cases for Mapper. What happens under high load, with invalid input, or during failures?

Solution
// Edge case handling:
// 1. Null/empty input -> validation
// 2. High load -> rate limiting, queuing
// 3. Failures -> retries, circuit breaker
// 4. Concurrent access -> locks, idempotency
Mapper Testing Strategy

Write a testing strategy for Mapper. Include unit tests, integration tests, and performance tests.

Solution
// Test plan:
// - Unit: 80% coverage target
// - Integration: API contracts
// - Performance: latency, throughput
// - Chaos: failure injection

Quiz

1. What is the advantage of MapStruct over manual mapping?

Question 1 options

2. When should you use manual mapping instead of MapStruct?

Question 2 options

3. What is the primary purpose of Mapper?

Question 3 options

4. What is a common mistake when implementing Mapper?

Question 4 options

Flashcards

Question

What does a mapper do?

Answer

Converts between entities and DTOs to keep layers decoupled

Question

MapStruct vs manual mapping?

Answer

MapStruct: compile-time, fast, type-safe. Manual: full control, simple cases

Question

What is Mapper?

Answer

Mapper is a key concept in backend development.

Question

When to use Mapper?

Answer

Use Mapper when building production systems that require reliability, scalability, and maintainability.

Question

Mapper best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.Mappers convert between entities and DTOs
  • 2.Manual mapping gives full control but is verbose
  • 3.MapStruct generates mapping code at compile time — fast and type-safe
  • 4.Use @Mapping for field name mismatches, @MappingTarget for updates

Interview Tips

  • Know when to use MapStruct vs manual mapping
  • Be ready to write a mapper for a given entity/DTO pair
  • Understand compile-time vs runtime mapping

Cheat Sheet

Mappers

  • Manual: Full control, verbose, good for simple cases
  • MapStruct: Compile-time, fast, type-safe, good for complex mappings
  • @Mapper(componentModel="spring"): Auto-registers as Spring bean
  • @Mapping(source, target): Maps field name mismatches