英文:
How to avoid almost repeatable model in clean architecture and make code follow DRY principle and dependency rule?
问题
我正在使用干净的架构构建电子商务项目,其中有4个层次,如下所示 -
- 应用层
- 用户界面层
- 领域层
- 基础架构层
还在应用层中使用CQRS模式。当要添加某项功能时,需要创建3种相似类型的模型。
- 领域层 - 实体 - 实体模型
- 应用层 - 模型 - 为命令/查询创建模型
- 用户界面层 - VM - 视图模型
由于干净架构的依赖规则,不能从较低层次访问模型。因此,需要从三个不同的层次创建相似类型的模型,这违反了DRY(不要重复自己)原则。
第二个问题是添加新功能需要编写大量代码。假设要添加简单的功能,需要执行以下操作:
- 在领域层中首先创建一个实体
- 在应用层中创建一个命令/查询(CQRS)
- 第三件事是需要为命令获取数据,然后再次创建请求或响应模型,为此在应用层中创建一个模型
- 创建一个视图并显示所需的数据,这又需要在用户界面层中创建一个新的模型,并需要另一个用于模型映射的方法。
请检查使用以下网址的文件夹结构 - <https://tree.nathanfriend.io/?s=(%27op2s!(%27fancyH~fullPath!false~trailYgSlashH~rootDotH)~W(%27W%27Solu23applica25Zfunc2s-8sKXKInsRt8X7KQuRyKGetAll8QuRy7KModelK8Detailz93UI5ZF-ProductF7bModel-8VM7-6-j-q-0ErrorMsgKP_%20p_b-87html3DomaY5ZEntityxz940IsDisplay40IsBlock40Title3PRsistance5xCJ7%20%2F%2Fentity%20cJs-3%27)~vRsion!%271%27)%20%20-Z0strYg%202tion3%5Cn4K**5%20LayR6Yt%20Id7.cs8Op29j4qFControllRH!trueJonfigura2K-RerWsource!XCommandYinZ3_agYa2bZViewj0Nameq0Typex-App8z7464%01zxqjb_ZYXWRKJHF987654320-*>
并检查以下三个模型 -
- OptionDetail.cs
- OptionVM.cs
- AppOption.cs
因此,我观察到我在三个不同的层次中创建了相似类型的对象,其中一个或两个属性不同,但其余属性都相似。
所以问题在于添加简单功能需要编写大量代码,大部分是创建模型和模型映射,这需要太多时间。
英文:
I building an e-commerce project using a clean architecture that has 4 layers as follows -
- Application layer
- UI Layer
- Domain layer
- Infrastrucure layer
Also using the CQRS pattern in the application layer. When something feature wants to add then it's required to create 3 similar kinds of models.
- Domain Layer - Entity - Entity model
- Application Layer - Model - create a model for command/ query
- UI Layer - VM - View Model
Due to the dependency rule of clean architecture, you cannot access the model from the lower layer. so need to create a similar kind of model from three different layers and its breaks the DRY (Don't Repeat Yourself) principle.
The second thing lot of code is required to write when adding new features. Let's say adding simple features following things need to create
- First create an entity in the domain layer
- Second create a command / Query (CQRS) in the application layer
- Third thing needs to require data for the command and then again create a request or response model for that create a model in the application layer
- Fourth create a view and show the data it requires again new model in the UI layer and requires another method for model mapping.
Please check folder structure using this Url - <https://tree.nathanfriend.io/?s=(%27op2s!(%27fancyH~fullPath!false~trailYgSlashH~rootDotH)~W(%27W%27Solu23applica25Zfunc2s-8sKXKInsRt8X7KQuRyKGetAll8QuRy7KModelK8Detailz93UI5ZF-ProductF7bModel-8VM7-6-j-q-0ErrorMsgKP_%20p_b-87html3DomaY5ZEntityxz940IsDisplay40IsBlock40Title3PRsistance5xCJ7%20%2F%2Fentity%20cJs-3%27)~vRsion!%271%27)%20%20-Z0strYg%202tion3%5Cn4K**5%20LayR6Yt%20Id7.cs8Op29j4qFControllRH!trueJonfigura2K-RerWsource!XCommandYinZ3_agYa2bZViewj0Nameq0Typex-App8z7464%01zxqjb_ZYXWRKJHF987654320-*>
And check following three models -
- OptionDetail.cs
- OptionVM.cs
- AppOption.cs
So I'm observing that I'm creating a similar kind of object in three different layers which have almost one or two property differences but the rest of the properties are similar.
So the problem is a lot of code require to write while adding simple feature and mostly create model and model mapping its take too much time.
答案1
得分: 0
可能让您感到惊讶的是,DRY原则并不意味着删除所有看起来相同的代码。David Thomas和Andrew Hunt在他们的书《The Pragmatic Programmer》中解释了DRY原则。他们说,DRY是关于知识重复的问题。在第2章“实用方法”中,第34页,他们提供了以下示例:
def validate_age(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
def validate_quantity(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
他们说:
在代码审查期间,那位自诩为全知全能的人批评这段代码,声称它违反了DRY原则:两个函数的函数体是相同的。
他们错了。代码是相同的,但它们所代表的知识是不同的。这两个函数验证了两个不同的事物,只是碰巧具有相同的规则。这是巧合,而不是重复。
Uncle Bob在他的干净架构书中的第7章“SRP单一责任原则”中描述了类似的问题。这个问题被描述为“症状1:意外的重复”。他说:
例如,假设
calculatePay()
函数和reportHours()
函数共享用于计算非加班小时的常规算法。假设开发人员非常小心地避免重复代码,将该算法放入一个名为regularHours
的函数中。
然后他解释了一个演员的更改,例如CFO,可能会意外地破坏COO感兴趣的代码。他的例子中的“重复”并不是真正的重复。就像在DRY原则的解释中一样,这只是巧合,而不是重复。
您在问题中提到的对象负责不同的事情。最内层的实体封装了与用例无关的业务规则。视图模型负责向用户呈现数据。请求和响应模型是用例的API的一部分。每个模型都有不同的目的。
您说:
所以我观察到我在三个不同的层中创建了一种类似的对象,它们几乎有一两个属性的不同,但其余的属性是相似的。
所以问题是在添加简单功能时需要编写大量的代码,大多数是创建模型和模型映射,这需要太多时间。
只需尝试将所有东西放在实体中,您将很快或很久之后看到问题所在。因为将所有东西放在一起意味着您只会有一个负责一切的对象。您甚至可以扩展该对象,并在持久层中使用它。如果您这样做,您将很快发现UI的更改很容易破坏持久性,反之亦然。
现在我们来讨论令人讨厌的部分。某些用例往往在所有层中具有非常相似的对象。所以我们往往会说:“哎呀,这是很多工作要做。”但是为了维护架构,我们应该付出努力。
所以我的建议是分开每个责任。
英文:
It might be suprising to you, but the DRY principle does not mean remove all code that looks the same. David Thomas and Andrew Hunt explain the DRY principle in their book "The Pragmatic Programmer". They say that DRY is about knowledge duplication. In chapter 2, A Pragmatic Approach, p. 34 they provide this example:
def validate_age(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
def validate_quantity(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
They say that
> During code review, the resident know-it-all bounces this code, claming it's > a DRY violation: both function bodies are the same.
>
> They are wrong. The code is the same, but the knowledge they represent is
> different. The two functions validate two separate things that just happen
> to have the same rules. That's coincidence, not a duplication.
Uncle Bob describes a similar issues in chapter 7, SRP The Single Responsibility Principle, in his clean architecture book. The issues is described as "Symptom 1: Accidentionl Duplication". He says
> For example, suppose that the calculatePay()
function and the reportHours()
function share a common algorithm for calculating non-overtime hours. Suppose that the developers, who are careful not to duplicate code, put that algorithm into a function named regularHours
.
Then he explains how changes from one actor, e.g. the CFO, can accidentially break code that the COO is interessted in. The "duplication" in his example wasn't a real duplication. Like in the explanation of the DRY principle, it was coincidence, not a duplication.
The objects you talked about in your question are responsible for differnt things. The entities in the inner most layer encapsulate use case agnostic business rules. The view models are responsible for how data is presented to the user. The request and response models are part of the api of the use case.
Each model has a different purpose.
You said that
> So I'm observing that I'm creating a similar kind of object in three
> different layers which have almost one or two property differences but the
> rest of the properties are similar.
>
> So the problem is a lot of code require to write while adding simple feature
> and mostly create model and model mapping its take too much time.
Just try to put all things together in the entity and you will see sooner or later what the problems are. Because putting everything together means that you will just have one object that is responsible for everything. You can even extend that object and also use it in the persistence layer. If you do that you will find out fast that a change to the UI can easily break the persistence and vice versa.
Now we come to the annoying part. There are some use cases which tend to have very similar objects in all layers. So we tend to say "Puuh, it's a lot of work to do.", but we should make the effort in order to maintain the architecture.
So my advice is to separate each responsibility.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论