在不同的API端点中为同一实体使用多个DTO是一种好习惯。例如: 我有一个api端点,它接受以下Dto:
public class AddressDto
{
public string City { get; set; }
public string Country { get; set; }
public string Contact { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
现在有第二个Api接受相同的dto,但在api通话中,我只使用Streer1, Street2, Contact
所有其他都被忽略。
我应该为第二个DTO
制作另一个api endpoint
,如:
public class AddressDtoForSecondAPI
{
public string Contact { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
}
答案 0 :(得分:1)
简而言之,是的,这是可以接受的。
但是,正如您在评论和其他答案中所看到的,并非所有人都同意这里。让我解释一下我的答案。
参数1 - 误导消费者
现在有第二个Api接受相同的dto,但在api通话中,我只使用
Streer1, Street2, Contact
所有其他都被忽略。
这里的问题是让你的意图明确。如果您允许消费者向您发送完全充实的AddressDTO
,但只使用属性的子集,那么您就会误导您的消费者。您已让他们认为其他属性是相关的。
这实际上与:
相同public int AddNumbersTogether(int a, int b, int c, int d)
{
return a + c + d; //we ignore b
}
b
没有理由存在。当AddNumbersTogether(1,2,3,4)
返回值8
时,使用此方法的任何人都会摸不着头脑。语法与行为相矛盾。
是的,省略未使用的方法参数比开发第二个DTO更容易。但是你需要在这里保持一致并坚持同样的原则:不要误导消费者。
参数2 - DTO不是实体
您的消费者与您的API的互动需要在消费者不了解您的数据库记录结构的情况下进行。
这就是您开始使用DTO而不是实体类的原因!您在采取行动和存储该行动的数据之间提供了逻辑上的分离。
消费者并不关心数据存储的位置。无论您是将街道存储在与地址相同的表中,还是将不同的表(或数据库)存储在同一个表中,在调用API方法的消费者范围内无关紧要。
参数3 - 反击S.Akbari
SOLID 中的继承和/或接口隔离原则怎么样? - S.Akbari
对于这种特殊情况,这些不是有效的参数。
继承是一种有缺陷的方法。是的,您可以技术在发布的示例代码中执行类似AddressDto : AddressDtoForSecondAPI
的操作,但这是一个巨大的代码味道。
当需要第三个DTO时会发生什么,例如一个只使用邮政编码和城市名称的地方?您无法AddressDto
从多个来源继承,AddressDtoForSecondAPI
与新创建的AddressDtoForThirdAPI
之间不存在逻辑重叠。
接口不是此处的解决方案。是的,您可以在技术上创建一个包含相应字段的IAddressDtoForSecondAPI
和IAddressDtoForThirdAPI
界面,然后执行AddressDto : IAddressDtoForSecondAPI, IAddressDtoForThirdAPI
之类的操作。然而,这又是一种巨大的代码味道。
如果第二个和第三个变体有一些共享属性,并且有几个单独的属性,会发生什么?如果应用接口隔离,则需要在接口中自行抽象重叠属性 如果那时第四个变体呈现出来,它有一些与第二个变化相同的属性,一些具有第三个变化,一些具有第二个和第三个变化,以及一些单独的属性,那么你将需要创建更多接口!
给出相同实体的足够变化并反复应用界面隔离原则;您将最终获得该实体的每个属性的接口;这需要大量的锅炉电镀。你最终会得到类似的东西:
公共类AddressDto:IAddressCity,IAddressCountry,IAddressContact,IAddressStreet1,IAddressStreet2,IAddressState,IAddressZip { public string City {get;组; } public string Country {get;组; } public string联系{get;组; } public string Street1 {get;组; } public string Street2 {get;组; } public string State {get;组; } public string Zip {get;组; } }
想象一下,必须为所有课程做到这一点;因为相同的原则适用于API使用的每个DTO。
参数4 - DRY不适用
我有点理解你为什么要担心创建两个课程。最有可能的是,你脑子里会出现DRY / WET错误标志。
避免WET是一种很好的反应;但你不能总是听它。因为如果你真的避免重复,那么你也应该有效地不创建单独的实体和DTO类,因为它们通常是彼此的复制/粘贴。
DRY不是绝对的。以实体/ DTO为例,这里有一个平衡点:
在这种情况下,后者通常会胜出。
同样的论点适用于您的情况。 DRY之后反对的参数(我刚刚列出的参数)远超过了在这种情况下跟随DRY的好处。