What is the right way to build an entity with relationships


I have some factory that’s responsible to build Product entity. To build the Product it’s necessary to retrieve all entities from a data source that should be associated with Product.

class ProductFactory(
    private val productRepository: ProductRepository,
    private val shopRepository: ShopRepository,
    private val categoryRepository: CategoryRepository,
    private val tagRepository: TagRepository
) {
    fun build(action: CreateProductDTO): Product {
         val product = Product.Builder()
         val shop = shopRepository.findById(action.shopId)

         val tags = tagRepository.findAllById(action.tags)

         val category = categoryRepository.findById(action.categoryId)

         return productRepository.save(builder.build())

Personally I don’t like the code above because of interface segregation principle violation at least. ProductFactory can access to all methods of the repositories but should not supposed to do this.

I have a thought to create some kind of DAL called Storage that could be used for specific business operation such as product creation. For example:

interface Storage {
    fun findShopById(id: Long): Optional<Shop>
    fun findCategoryById(id: Long): Optional<Category>
    fun findAllTagsById(ids: Iterable<Long>): List<Tag>
    fun save(product: Product)

Any suggestions?


The interface segregation principle that you want to apply is a good idea. It makes testing much easier, because you only need one mock and and not a whole bunch of mocks.

I would name the interface after the client that it is dedicated to, e.g. ProductFactoryRepository.

The code that your ProductRepository implements seems to be code that I usually would write in an interactor (aka. use case). Of cource you can extract it to an own class if you want.

But there is one thing that might break the architecture (if I understand your code). It is this function.

 fun build(action: CreateProductDTO): Product {

As far as I understand you. The ProductFactory is part of the entity (or domain) layer. The CreateProductDTO seems to belong to the controller (web or transport) layer, because DTO usually stands for data transfer object.

But this would mean that you have a dependency from the entity layer to the transport layer, which breaks the architecture rules of the clean architecture.

The clean architecture proposes to pass plain data structures into an InputPort. These data structures are often called RequestModel and ResponseModel.

The interactor should implement an input port such as this:

interface CreateProductInputPort {
    fun createProduct(requestModel: CreateProductRequestModel, outputPort: CreateProductOutputPort)

The RequestModel can either be a simple data like

data class CreateProductRequestModel(val shopId: Int, val tags: Array<String>, val categoryId: Int)

or you can declare an interface

interface CreateProductRequestModel {
   fun getShopId(): Int
   fun getTags(): Array<String>
   fun getCategoryId(): Int

and let CreateProductDTO implement it.

You should decouple the interactor (use case) from the transport layer in order to apply the single responsibility principle, because the DTO changes for other reasons than the use cases input model.

PS: I hope the Kotlin code I wrote is correct. I usually code in Java.

Answered By – René Link

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published