I struggle with principled DDD in a layered (controller, service, repository) application. When trying to seek answers online, I seem to get a lot of conflicting information/opinions. See my example below:
Domain
public class Customer {
private Long id;
private String name;
}
public class CustomerOrder {
private Long id;
private String productName;
private int quantity;
private Customer customer;
}
Entity
@Entity
public class CustomerEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
@Entity
public class CustomerOrderEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productName;
private int quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
private CustomerEntity customer;
}
DTO
@Data
public class CustomerOrderRequestDTO {
private Long customerId;
private String productName;
private int quantity;
}
@Data
public class CustomerOrderResponseDTO {
private Long orderId;
private String productName;
private int quantity;
private Long customerId;
private String customerName;
}
Controller layer
@RestController
@RequestMapping("/api/orders")
@AllArgsConstructor
public class CustomerOrderController {
private final CustomerOrderService customerOrderService;
@PostMapping
public ResponseEntity<CustomerOrderResponseDTO> createOrder(@RequestBody CustomerOrderRequestDTO requestDTO) {
CustomerOrderResponseDTO responseDTO = customerOrderService.createOrder(requestDTO);
return ResponseEntity.ok(responseDTO);
}
}
Service layer
@Service
@AllArgsConstructor
public class CustomerOrderService {
private final CustomerRepository customerRepository;
private final CustomerOrderRepository customerOrderRepository;
private final Mapper mapper;
public CustomerOrderResponseDTO createOrder(CustomerOrderRequestDTO requestDTO) {
CustomerEntity customerEntity = customerRepository.findById(requestDTO.getCustomerId())
.orElseThrow(() -> new IllegalArgumentException("Customer not found"));
Customer customer = mapper.map(customerEntity);
CustomerOrder customerOrder = mapper.map(requestDTO, customer);
//maybe do some validation on the domain instances
CustomerOrderEntity customerOrderEntity = mapper.map(customerOrder);
customerOrderRepository.save(customerOrderEntity);
CustomerOrderResponseDTO responseDTO = mapper.map(customerOrder);
return responseDTO;
}
}
Repository layer
Just some default JpaRepostories.
Questions
Specific questions:
- Should we get the Customer the customerRepository + mapping, or should we retrieve it using a CustomerService? If so, shouldn’t that CustomerService return a DTO instead of a domain object?
- The service layer currently performs multiple mappings between entities, domain objects, and DTOs. Is this approach aligned with DDD principles, or should the mapping be done elsewhere (e.g., in the repository or controller)?
- When creating the CustomerOrderResponseDTO, should I map from the CustomerOrderEntity or from the CustomerOrder?
General question:
- Is the example above a principled design? If not, what would you change?
New contributor
Gman is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.