ServiceStack请求和响应对象
是否可以(阅读良好实践)重复使用POCO进行请求和响应DTO。 我们的POCO是轻量级的(ORM Lite),只有属性和一些装饰属性。
或者,我应该为请求和/或响应创建其他对象吗?
谢谢,
我会说这取决于你的系统,最终它有多大和复杂,并且有可能成为。
ServiceStack文档未指定您应使用的设计模式。 最终,它提供了将数据库模型POCO与DTO分离的灵活性,但它也为它们的重用提供了支持。
使用OrmLite时:
OrmLite的设计使您可以重复使用数据模型POCO作为请求和响应DTO。 从ServiceStack文档中可以看出 ,这是框架的有意设计目标:
Micro ORMS中使用的POCO特别适合作为DTO重新使用,因为它们不包含重ORM具有的任何循环引用(例如EF)。 OrmLite更进一步,从NoSQL的playbook中借用页面,其中任何复杂的属性,例如List在无模式文本字段中透明地填充,促进了RDBMS关注不受限制的无摩擦Pure POCOS的设计。
考虑:
如果您选择重新使用POCO,因为它受支持,您应该知道在某些情况下使用单独的请求和响应DTO会更聪明。
在许多情况下,这些POCO数据模型已经形成了良好的DTO,可以直接返回,而不是映射到特定于域的DTO。
^并非所有情况 。 有时,选择设计模式的难度预计会出现可能不适合重复使用的情况。 所以希望情景有助于说明潜在的问题。
场景:
- 您有一个系统,用户可以在其中注册您的服务。
- 作为管理员,您可以列出服务的用户。
如果你采用OrmLite POCO重用方法,那么我们可能会有这个User
POCO:
public class User { [PrimaryKey, AutoIncrement, Alias("Id")] public int UserId { get; set; } public string Username { get; set; } public string Password { get; set; } public string Salt { get; set; } public bool Enabled { get; set; } }
当您创建“创建用户”请求时,您将User
POCO的Username
和Password
填充为对服务器的请求。
我们不能将此POCO推送到数据库中,因为:
-
Password
字段中的Password
为纯文本。 我们是优秀的程序员,安全性很重要,因此我们需要创建一个salt,我们将其添加到Salt
属性中,并使用salt哈希Password
并更新Password
字段。 好的,这不是一个主要问题,几行代码将在插入之前对其进行排序。 -
客户端可能已设置
UserId
,但对于create,这不是必需的,并且将导致我们的数据库查询无法插入。 因此我们必须在插入数据库之前默认此值。 -
Enabled
属性可能已与请求一起传递。 如果有人设置了这个怎么办? 我们只想要处理Username
和Password
,但现在我们必须考虑影响数据库插入的其他字段。 同样,他们可以设置Salt
(虽然这不会是一个问题,因为我们无论如何都会覆盖这个值。)所以现在你已经添加了validation。
但现在考虑我们何时返回List
。
如果您重新使用POCO作为响应类型,则有许多字段不希望暴露给客户端。 做起来并不聪明:
return Db.Select();
由于您没有用于列出用户的紧密专用响应,因此需要在逻辑中删除Password
哈希和Salt
,以防止在响应中将其序列化。
还要考虑在注册用户期间,作为创建请求的一部分,我们想询问是否应该发送欢迎电子邮件。 所以我们会更新POCO:
public class User { // Properties as before ... [Ignore] // This isn't a database field public bool SendWelcomeEmail { get; set; } }
我们现在拥有仅在用户创建过程中有用的附加属性。 如果您反复使用User
POCO,您会发现随着时间的推移,您添加的内容越来越多,不适用于某些上下文。
例如,当我们返回用户列表时,现在可以填充SendWelcomeEmail
的可选属性 – 它只是没有意义。 然后很难维护代码。
要记住的一个关键事项是,在共享POCO对象时,它既可以用作请求,也可以用作响应对象: 作为响应发送的属性将在请求中公开。 您将不得不对请求进行更多validation,最终共享POCO可能无法节省成本。
在这种情况下,这样做不会容易得多:
public class CreateUserRequest { public string Username { get; set; } public string Password { get; set; } public bool SendWelcomeEmail { get; set; } } public class UserResponse { public int UserId { get; set; } public string Username { get; set; } public bool Enabled { get; set; } } public class User { [PrimaryKey, AutoIncrement, Alias("Id")] public int UserId { get; set; } public string Username { get; set; } public string Password { get; set; } public string Salt { get; set; } public bool Enabled { get; set; } }
我们现在知道当我们创建一个请求( CreateUserRequest
)时,我们不必考虑UserId
, Salt
或Enabled
。
当返回用户列表时,它现在是List
,客户端将无法看到我们不希望他们看到的任何属性。
对于查看代码,请求所需属性以及响应中将公开的内容的其他人来说,这一点很清楚。
摘要:
对不起,这是一个非常长的答案,但我认为这解决了分享POCO的一个方面,有些人错过了,或者最初没有掌握,我就是其中之一。
- 是的,您可以重复使用POCO进行请求和响应。
- 文档说这样做是可以的。 实际上它是设计的。
- 在许多情况下,重复使用会很好。
- 有些情况下它不合适。 (我的场景试图展示这一点,但是当你发展自己的真实情况时,你会发现。)
- 考虑可能会暴露多少其他属性,因为您的共享POCO尝试支持多个操作,以及可能需要多少额外的validation工作。
- 最终,这是关于你维护的舒适度。
希望这可以帮助。
我们有其他方法,我的答案是固执己见的。
因为我们不仅使用C#客户端,而且主要使用JavaScript客户端。
请求和响应DTO,路由和数据实体之间进行协商
客户和前端分析师。 它们是详细forms的规格的一部分。
即使“客户”,在某些情况下,也是我们的产品UI。
这些DTO在没有重要原因的情况下不会改变,并且可以在双方重复使用。
但是数据层中的对象可以是相同或部分类或不同,
它们可以在内部更改,包括敏感或工作流信息,
但它们必须与API的规范兼容。
我们首先使用API ,而不是数据库或ORM。
Person { ... } Address { ... } ResponceDTO { public bool success {get; set;} public string message {get; set;} public Person person {get; set;} public List addresses {get; set;} //customer can use the Person & Address, which can be the same or different //with the ones in the data-layer. But we have defined these POCO's in the specs. } RequestDTO { public int Id {get; set;} public FilteByAge {get; set;} public FilteByZipCode {get; set;} } UpdatePersonRequest { public int Id {get; set;} public bool IsNew {get; set;} public Person person {get; set;} public List addresses {get; set;} }
我们不公开请求或响应DTO。
人员和地址与客户协商并在API规范中引用。
它们可以与数据层内部实现相同或部分或不同。
客户将使用它们到他们的应用程序或网站或移动设备。
但重要的是我们首先设计和协商API接口。
我们经常使用requestDTO作为业务层函数的参数,
返回响应对象或集合。
通过这种方式,服务代码是业务层前面的瘦包装器。
ResponseDTO Get(RequestDTO request) { return GetPersonData(request); }
同样来自ServiceStack wiki, API-First开发方法
鉴于您可以公开数据对象的结构(如果这是一个公开使用的API),这不会是一个问题。 否则,Restsharp将与简单的POCO一起使用:)
我认为这一切都取决于你如何使用你的DTO,以及你希望如何平衡代码的可重用性和可读性。 如果您的请求和响应都使用了DTO上的大部分属性,那么您将获得大量的可重用性,而不会降低可读性。 例如,如果您的请求对象有10个属性(反之亦然),但您的响应只需要其中的1个,那么如果您的响应对象上只有1个属性,则有人可以提出一个更容易理解/读取的参数。
总之,良好的做法就是干净的代码。 您必须根据您的代码是否易于使用和阅读来评估您的特定用例。 另一种思考方式是为下一个阅读它的人编写代码,即使那个人就是你。