⬅️ Back to Layers Overview

Integration with Other Layers

Layer Architecture Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  CONTROLLER LAYER                            β”‚
β”‚              (HTTP Requests/Responses)                        β”‚
β”‚  HTTP β†’ DTO β†’ Service Call β†’ Response DTO β†’ JSON             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚ Calls ServiceInterface
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  SERVICE LAYER                               β”‚
β”‚           (Business Logic & Orchestration)                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ β€’ Validates input                                      β”‚ β”‚
β”‚  β”‚ β€’ Transforms DTO ↔ Entity                              β”‚ β”‚
β”‚  β”‚ β€’ Executes business logic                              β”‚ β”‚
β”‚  β”‚ β€’ Manages transactions                                 β”‚ β”‚
β”‚  β”‚ β€’ Logs changes (audit)                                 β”‚ β”‚
β”‚  β”‚ β€’ Throws domain exceptions                             β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Dependencies Injected:                                 β”‚ β”‚
β”‚  β”‚ β€’ Repository (data access)                             β”‚ β”‚
β”‚  β”‚ β€’ Validators (business rules)                          β”‚ β”‚
β”‚  β”‚ β€’ Mappers (DTO ↔ Entity)                               β”‚ β”‚
β”‚  β”‚ β€’ Other Services (orchestration)                       β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚ Calls Repository
                 β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              REPOSITORY LAYER                                 β”‚
β”‚         (Data Access Abstraction)                             β”‚
β”‚  JPA Interface β†’ Hibernate β†’ SQL β†’ Database                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Integration Points

1. Controllers Call Services

Controllers invoke service methods and return service results:

@RestController
@RequestMapping("/api/suppliers")
@RequiredArgsConstructor
public class SupplierController {
    
    private final SupplierService service;
    
    @PostMapping
    public ResponseEntity<SupplierDTO> create(
            @RequestBody CreateSupplierDTO dto) {
        // Controller delegates to service
        SupplierDTO result = service.create(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<SupplierDTO> getById(@PathVariable String id) {
        return ResponseEntity.ok(
            service.findById(id)
                .orElseThrow(() -> new NoSuchElementException("Not found"))
        );
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<SupplierDTO> update(
            @PathVariable String id,
            @RequestBody UpdateSupplierDTO dto) {
        return ResponseEntity.ok(service.update(id, dto));
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable String id) {
        service.delete(id);
        return ResponseEntity.noContent().build();
    }
}

2. Services Use Repositories

Services call repositories to interact with database:

@Service
@RequiredArgsConstructor
public class SupplierServiceImpl implements SupplierService {
    
    private final SupplierRepository repository;
    private final SupplierValidator validator;
    private final SupplierMapper mapper;
    
    @Transactional
    public SupplierDTO create(CreateSupplierDTO dto) {
        // Validation layer
        validator.validateRequiredFields(dto);
        validator.validateUniquenessOnCreate(dto.getName());
        
        // Data mapper
        Supplier entity = mapper.toEntity(dto);
        
        // Repository layer
        Supplier saved = repository.save(entity);
        
        return mapper.toDTO(saved);
    }
}

3. Validators Enforce Business Rules

Services delegate business rule validation:

@Component
@RequiredArgsConstructor
public class SupplierValidator {
    
    private final SupplierRepository repository;
    
    public void validateUniquenessOnCreate(String name) {
        if (repository.existsByNameIgnoreCase(name)) {
            throw new IllegalStateException("Duplicate supplier");
        }
    }
}

4. Mappers Transform DTOs to Entities

Services use mappers for boundary transformations:

@Mapper(componentModel = "spring")
public interface SupplierMapper {
    
    // Inbound: API DTO β†’ Domain Entity
    Supplier toEntity(CreateSupplierDTO dto);
    
    // Outbound: Domain Entity β†’ API DTO
    SupplierDTO toDTO(Supplier entity);
}

5. Global Exception Handler Catches Service Exceptions

Services throw domain exceptions caught by controller advice:

@Service
public class SupplierServiceImpl {
    public SupplierDTO create(CreateSupplierDTO dto) {
        if (duplicate) {
            throw new IllegalStateException("Duplicate");  // Thrown
        }
        return ...;
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(IllegalStateException.class)
    public ResponseEntity<ErrorResponse> handleConflict(
            IllegalStateException ex) {
        return ResponseEntity.status(HttpStatus.CONFLICT)
            .body(new ErrorResponse("CONFLICT", ex.getMessage()));
    }
}

Data Flow Example: Create Supplier

Complete flow from HTTP request to database persistence:

1. HTTP Request
   POST /api/suppliers
   {"name": "TechCorp", "contactName": "John Doe"}
   
2. Controller Deserialization
   CreateSupplierDTO {
     name: "TechCorp"
     contactName: "John Doe"
   }
   
3. Controller β†’ Service
   SupplierService.create(dto)
   
4. Service Validation
   SupplierValidator.validateRequiredFields(dto)
   SupplierValidator.validateUniquenessOnCreate(name)
   
5. Service Transformation
   Mapper.toEntity(dto) β†’ Supplier entity
   
6. Service Audit
   entity.setCreatedBy("john.doe")
   entity.setCreatedAt(now)
   
7. Service β†’ Repository
   SupplierRepository.save(entity)
   
8. Database Persistence
   INSERT INTO SUPPLIER (ID, NAME, CONTACT_NAME, CREATED_BY, CREATED_AT)
   VALUES ('uuid-123', 'TechCorp', 'John Doe', 'john.doe', '2025-01-15 10:30:00')
   
9. Entity Returned
   Supplier {
     id: 'uuid-123'
     name: 'TechCorp'
     contactName: 'John Doe'
     createdBy: 'john.doe'
     createdAt: '2025-01-15 10:30:00'
   }
   
10. Service Response Transformation
    Mapper.toDTO(entity) β†’ SupplierDTO
    
11. Controller Response
    200 OK (actually 201 Created)
    {"id": "uuid-123", "name": "TechCorp", ...}
    
12. HTTP Response
    JSON sent to client

Services Calling Other Services

Services can depend on other services for complex operations:

@Service
@RequiredArgsConstructor
@Transactional
public class InventoryItemServiceImpl implements InventoryItemService {
    
    private final InventoryItemRepository itemRepository;
    private final StockHistoryService stockHistoryService;
    
    public InventoryItemDTO create(CreateInventoryItemDTO dto) {
        // Create the item
        InventoryItem item = mapper.toEntity(dto);
        InventoryItem saved = itemRepository.save(item);
        
        // Call another service within same transaction
        stockHistoryService.logInitialStock(saved);
        
        return mapper.toDTO(saved);
    }
}

@Service
@RequiredArgsConstructor
public class StockHistoryServiceImpl implements StockHistoryService {
    
    private final StockHistoryRepository repository;
    
    @Transactional
    public void logInitialStock(InventoryItem item) {
        StockHistory history = new StockHistory();
        history.setItem(item);
        history.setReason(StockChangeReason.ADJUSTMENT);
        history.setCreatedBy(getCurrentUsername());
        
        repository.save(history);
    }
}

⬅️ Back to Layers Overview