建立与人际关系的实体的正确方法是什么
我有一些工厂负责构建产品
实体。要构建产品
有必要从应与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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您要应用的界面隔离原则是一个好主意。它使测试变得更加容易,因为您只需要一个模拟,而不需要一堆模拟。
我将界面命名为客户端的客户端,例如
productFactoryRepository
。您的
productTrepository
实现的代码似乎是我通常会在交互式中写的代码(又称用例)。库尔斯(Cource),您可以根据需要将其提取到自己的课程中。但是,有一件事可能会破坏架构(如果我理解您的代码)。这是这个功能。
据我了解你。
ProductFactory
是实体(或域)层的一部分。createProductDto
似乎属于控制器(Web或传输)层,因为DTO通常代表数据传输对象。但这意味着您具有从实体层到传输层的依赖性,这破坏了干净体系结构的体系结构。
干净的体系结构建议将普通数据结构传递到
inputport
中。这些数据结构通常称为requestModel
和exponsememodel
。Interactor应实现这样的输入端口:
requestModel
可以是一个简单的数据,或者您可以声明接口
,然后让
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.
As far as I understand you. The
ProductFactory
is part of the entity (or domain) layer. TheCreateProductDTO
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 calledRequestModel
andResponseModel
.The interactor should implement an input port such as this:
The
RequestModel
can either be a simple data likeor you can declare an interface
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.
您正在混淆 Intantiation 和插入概念:
在DDD中,A Factory 是负责实体实例化的对象。实体插入是存储库的责任。在大多数情况下,您无需编写工厂,因为您可以编写一些简单的代码,例如
product product = new Product();
或使用一些映射库来投影数据持久性对象(DPO )进入实体。但是,有时候,对象实例化可能会成为一项复杂的任务,您可以编写工厂来考虑此代码并从不同类别重复使用。代替编写
产品=新产品();
而不是编写product product = productFactory.makeproduct(...);
和注入工厂之类的东西。这意味着您的
productFactory
实际上应该是您的Builder
类:You are confusing instantiation and insertion concepts here:
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 likeProduct product = productFactory.MakeProduct(...);
and inject the factory.This means your
ProductFactory
should actually be yourBuilder
class:遵循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 theShop
and can access only API that exposed by your domain (through the service query/commands)