建立与人际关系的实体的正确方法是什么

发布于 2025-01-29 23:27:12 字数 1206 浏览 3 评论 0原文

我有一些工厂负责构建产品实体。要构建产品有必要从应与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)
         product.setShop(shop)

         val tags = tagRepository.findAllById(action.tags)
         product.setTags(tags)

         val category = categoryRepository.findById(action.categoryId)
         product.setTaxon(taxon)

         return productRepository.save(builder.build())
    }
}

我个人至少违反了界面隔离原则,因此我不喜欢上面的代码。 productFactory可以访问存储库的所有方法,但不应该这样做。

我想创建某种称为存储的DAL,可用于特定业务操作,例如产品创建。例如:

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)
}

有建议吗?

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)
         product.setShop(shop)

         val tags = tagRepository.findAllById(action.tags)
         product.setTags(tags)

         val category = categoryRepository.findById(action.categoryId)
         product.setTaxon(taxon)

         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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怪异←思 2025-02-05 23:27:12

您要应用的界面隔离原则是一个好主意。它使测试变得更加容易,因为您只需要一个模拟,而不需要一堆模拟。

我将界面命名为客户端的客户端,例如productFactoryRepository

您的productTrepository实现的代码似乎是我通常会在交互式中写的代码(又称用例)。库尔斯(Cource),您可以根据需要将其提取到自己的课程中。

但是,有一件事可能会破坏架构(如果我理解您的代码)。这是这个功能。

 fun build(action: CreateProductDTO): Product {

据我了解你。 ProductFactory是实体(或域)层的一部分。 createProductDto似乎属于控制器(Web或传输)层,因为DTO通常代表数据传输对象。

但这意味着您具有从实体层到传输层的依赖性,这破坏了干净体系结构的体系结构。

干净的体系结构建议将普通数据结构传递到inputport中。这些数据结构通常称为requestModelexponsememodel

Interactor应实现这样的输入端口:

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

requestModel可以是一个简单的数据

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

,或者您可以声明接口

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

,然后让createProductdto实现它。

您应该将交互式(用例)与传输层分解以应用单个职责原则,因为除用例输入模型以外的其他原因,DTO会更改。

PS:我希望我写的Kotlin代码正确。我通常在Java中代码。

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.

笑忘罢 2025-02-05 23:27:12

您正在混淆 Intantiation 插入概念:

  • insertion 是当您在系统中添加新实体时发生的逻辑操作,例如添加一个产品目录的新产品。
  • Intantiation 是一个技术操作,当您想在内存中创建一个新对象时,例如将DTO转换为实体,或者您从数据库中检索现有产品时。

在DDD中,A Factory 是负责实体实例化的对象。实体插入存储库的责任。在大多数情况下,您无需编写工厂,因为您可以编写一些简单的代码,例如product product = new Product();或使用一些映射库来投影数据持久性对象(DPO )进入实体。

但是,有时候,对象实例化可能会成为一项复杂的任务,您可以编写工厂来考虑此代码并从不同类别重复使用。代替编写产品=新产品();而不是编写product product = productFactory.makeproduct(...);和注入工厂之类的东西。

这意味着您的productFactory实际上应该是您的Builder类:

class ProductRepository(
    private val productFactory: ProductFactory,
    private val shopRepository: ShopRepository,
    private val categoryRepository: CategoryRepository,
    private val tagRepository: TagRepository
) {
    fun insert(action: CreateProductDTO): Product {
         val shop = shopRepository.findById(action.shopId)
         val tags = tagRepository.findAllById(action.tags)
         val category = categoryRepository.findById(action.categoryId)
         val product = productFactory.MakeProduct(shop, tags, category)
         product.Name = action.productName
         return this.save(product)
    }
}

You are confusing instantiation and insertion concepts here:

  • Insertion is a logical operation which happens when you add a new entity into your system, like adding a new product to the product catalog.
  • Instantiation is a technical operation which happens when you want to create a new object in memory, like when you convert a DTO into an entity, or you whant to retrieve an existing product from the database.

In DDD, a factory is an object responsible for an entity instantiation. Entity insertion is the responsibility of the repository. Most of the time, you don't need to write a factory because you can write some simple code like Product product = new Product(); or use some mapping library to project a data persistence object (DPO) into an entity.

Sometimes though, object instantiation can become a complex task, and you can write a factory to factor this code and reuse it from different classes. Instead of writing Product product = new Product(); you would write something like Product product = productFactory.MakeProduct(...); and inject the factory.

This means your ProductFactory should actually be your Builder class:

class ProductRepository(
    private val productFactory: ProductFactory,
    private val shopRepository: ShopRepository,
    private val categoryRepository: CategoryRepository,
    private val tagRepository: TagRepository
) {
    fun insert(action: CreateProductDTO): Product {
         val shop = shopRepository.findById(action.shopId)
         val tags = tagRepository.findAllById(action.tags)
         val category = categoryRepository.findById(action.categoryId)
         val product = productFactory.MakeProduct(shop, tags, category)
         product.Name = action.productName
         return this.save(product)
    }
}
早乙女 2025-02-05 23:27:12

遵循DDD教条,两个聚合物应使用ID互相转介,而不是在产品聚合中设置整个商店/标签/类别:)这就是为什么该代码对您感到奇怪的原因,破坏该规则完全使用了使用存储库的必要性。

我想,使用整个shop是由您使用的持久性技术驱动的。
您有兴趣在工厂中做的是检查具有该ID的商店是否真正存在。

应当处理查询(将服务传递)到您的productFactory,以便将其与shop> shop> shop的详细信息解耦,并且只能访问由您的暴露的API域(通过服务查询/命令)

Following the DDD dogma, two aggregates should refer each other using ids, not setting the entire Shop/Tag/Category inside the Product aggregate :) that's why that code feels odd to you, breaking that rule create the necessity for using the repositories at all.

Using the entire Shop is driven by the technology you are using for persistence, I suppose.
What you are interested in doing in the factory is checking if a store with that id actually exists or not.

That should be handled executing a query (passing a service) to your ProductFactory so that it will be decoupled from the details of the Shop and can access only API that exposed by your domain (through the service query/commands)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文