I am recently working on hexagonal architecture test with java and spring boot, and i am trying to implement DTOs, i have a service class and this class inherits from four interfaces, ICreateProduct, IUpdateProduct, IRetrieveProduct and IDeleteProduct. The problem is that when i try to convert my domain models to DTOs to map the data i get an error, and i need change the methods to reurn DTOs instead of interface methods to resolve it. My question is for all those who have professional experience in hexagonal architecture, is it correct to change service methods to methods that return DTOs and only use the interfaces as dependency injection?
This is my code:
public class ProductService implements ICreateProduct, IUpdateProduct, IDeleteProduct{
private final ICreateProduct createProduct;
private final IUpdateProduct updateProduct;
private final IRetrieveProduct retrieveProduct;
private final IDeleteProduct deleteProduct;
public ProductService(ICreateProduct createProduct, IUpdateProduct updateProduct, IRetrieveProduct retrieveProduct, IDeleteProduct deleteProduct) {
this.createProduct = createProduct;
this.updateProduct = updateProduct;
this.retrieveProduct = retrieveProduct;
this.deleteProduct = deleteProduct;
}
@Override
public boolean deleteProductById(Long id) {
return deleteProduct.deleteProductById(id);
}
public GetAllProductsDTO getAllProducts() {
List<ProductDto> products = retrieveProduct.getAllProducts().stream().map(ProductMapper::toDto).collect(Collectors.toList());
//return retrieveProduct.getAllProducts().stream().map(this::convertToDTO).collect(Collectors.toList());
return new GetAllProductsDTO(products);
}
public ProductDto getProductById(Long id) {
Product product = retrieveProduct.getProductById(id).orElseThrow(() -> new RuntimeException("¡Product not found!"));
return ProductMapper.toDto(product);
}
@Override
public Optional<Product> updateProduct(Long id, Product product) {
return updateProduct.updateProduct(id, product);
}
@Override
public Product createProduct(Product product) {
return createProduct.createProduct(product);
}
}
And my DTOs, i am using records for that.
public record GetAllProductsDTO(List<ProductDto> products) {
}
public record ProductDto(Long id, String name, double price) {
}
These are the folders of my project, this is how it is structured.
I am recently working on hexagonal architecture test with java and spring boot, and i am trying to implement DTOs, i have a service class and this class inherits from four interfaces, ICreateProduct, IUpdateProduct, IRetrieveProduct and IDeleteProduct. The problem is that when i try to convert my domain models to DTOs to map the data i get an error, and i need change the methods to reurn DTOs instead of interface methods to resolve it. My question is for all those who have professional experience in hexagonal architecture, is it correct to change service methods to methods that return DTOs and only use the interfaces as dependency injection?
This is my code:
public class ProductService implements ICreateProduct, IUpdateProduct, IDeleteProduct{
private final ICreateProduct createProduct;
private final IUpdateProduct updateProduct;
private final IRetrieveProduct retrieveProduct;
private final IDeleteProduct deleteProduct;
public ProductService(ICreateProduct createProduct, IUpdateProduct updateProduct, IRetrieveProduct retrieveProduct, IDeleteProduct deleteProduct) {
this.createProduct = createProduct;
this.updateProduct = updateProduct;
this.retrieveProduct = retrieveProduct;
this.deleteProduct = deleteProduct;
}
@Override
public boolean deleteProductById(Long id) {
return deleteProduct.deleteProductById(id);
}
public GetAllProductsDTO getAllProducts() {
List<ProductDto> products = retrieveProduct.getAllProducts().stream().map(ProductMapper::toDto).collect(Collectors.toList());
//return retrieveProduct.getAllProducts().stream().map(this::convertToDTO).collect(Collectors.toList());
return new GetAllProductsDTO(products);
}
public ProductDto getProductById(Long id) {
Product product = retrieveProduct.getProductById(id).orElseThrow(() -> new RuntimeException("¡Product not found!"));
return ProductMapper.toDto(product);
}
@Override
public Optional<Product> updateProduct(Long id, Product product) {
return updateProduct.updateProduct(id, product);
}
@Override
public Product createProduct(Product product) {
return createProduct.createProduct(product);
}
}
And my DTOs, i am using records for that.
public record GetAllProductsDTO(List<ProductDto> products) {
}
public record ProductDto(Long id, String name, double price) {
}
These are the folders of my project, this is how it is structured.
Share Improve this question edited Mar 23 at 19:53 Frank2497 asked Mar 19 at 14:37 Frank2497Frank2497 92 bronze badges 8- 3 If you change the port interfaces to return DTOs, you leak adapter-specific concerns (DTOs) into the domain layer, violating hexagonal principles. – K.Nicholas Commented Mar 19 at 20:38
- 1 Please elaborate on the "i get an error". Could you provide your code "giving an error" and your dependency tree. A minimal reproducible example would be perfect. – Vincent C. Commented Mar 19 at 20:44
- @K.Nicholas I didn't change the interface ports to return DTOs, but as in the example, the service implemented the IRetrieveProduct interfaces as a contract, but when creating the DTOs I had to change the RetrieveProduct methods within the service to methods that would return the DTOs to the controller. – Frank2497 Commented Mar 20 at 0:23
- 2 could you add a architecture image? that would greatly improve the question. – Martin Frank Commented Mar 20 at 14:17
- 1 hello its me again, thanks for sharing your project structure, but i would like to see a view of the concepts, of the ideas, of the architecture to better understand your needs and problems - maybe you have an UML diagramm representing the idea of your app? – Martin Frank Commented Mar 24 at 8:21
1 Answer
Reset to default 0Yes, you can return DTOs to the controller, but it is better to return a common response body from the controller. Here's an example of how you can implement it:
public interface CommonService <T> {
T getById(String id);
List<T> getAll();
T create(T k);
T update(String id,T t) throws Exception;
PageableDto<T> search(Object o);
void deleteById(String id) throws Exception;
}
Then you can use this in your controller like this:
@RequestMapping("/test")
@RestController
public class Controller {
private final CommonService<TestDTO> commonService;
@PostMapping("")
public ApiCommonResponse create(@RequestBody TestDTO testDto) {
return new ApiCommonResponse(commonService.create(activityDto),
"200", "", "Data Created Successfully");
}
}
And ApiCommonResponse
is
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiCommonResponse {
private Object data;
private String timestamp=new Date().toString();
private String status;
private String error;
private String message;
}