【问题标题】:ServiceStack zero dependency Request-Response DTOsServiceStack 零依赖请求-响应 DTO
【发布时间】:2016-01-08 11:15:21
【问题描述】:

在阅读了一些 ServiceStack wiki 之后,我遇到了关于 DTO 的问题,希望您能提供帮助。

维基说:

  1. 在服务开发中,您的服务 DTO 为您提供与技术无关的服务层,您希望保持清洁并尽可能“无依赖”,以实现最大的可访问性和潜在的重用。我们的建议是将您的服务 DTO 保留在一个单独的、很大程度上无 dep 的程序集中。 (https://github.com/ServiceStack/ServiceStack/wiki/New-API)

  2. 最后,您还可以使用之前更明确的客户端 API(当您没有 IReturn 标记时非常理想): (https://github.com/ServiceStack/ServiceStack/wiki/New-API)

根据以上原因,我认为ServiceStack的最佳实践是: 我们应该使用 POCO 请求响应 DTO,而不是继承自 IReturn。

例如:

我们应该使用 1#:

public class AuthenticationRequest
{
    public string Name { get; set; }   
    public string Password { get; set; }
}

public class AuthenticationResponse
{
   public AuthenticationResponseType Result { get; set; }
   public UserInfoDto UserInfo { get; set; }
}

我们不应该使用 2#:

using ServiceStack;
public class AuthenticationRequest : IReturn<AuthenticationResponse>
{
    public string Name { get; set; }
    public string Password { get; set; }
}

public class AuthenticationResponse
{
   public AuthenticationResponseType Result { get; set; }
   public UserInfoDto UserInfo { get; set; }
}

因为1#是零依赖,所以2#对ServiceStack库/框架有依赖。

如果我将所有 Request-Response DTO 打包到一个 NET DLL 中,1# 比 2# 更抽象!

这意味着: 如果将来有一天我决定不使用 ServiceStack,这个 DLL 不需要任何更改。 (ServiceStack 库/框架应该是 Infrastructure 而不是 Abstraction)

如果我错了,请纠正我。

非常感谢。

【问题讨论】:

    标签: dependency-injection architecture domain-driven-design servicestack dto


    【解决方案1】:

    DTO 应该具有的唯一依赖项是 impl-free ServiceStack.Interfaces.dll,因为它是一个可移植类库 (PCL),几乎支持 .NET 运行的所有 mobile or Desktop platform。 ServiceStack 的 Interfaces .dll 是必需的,以便能够在单个良性 .dll 中清晰地描述您的完整服务合同。

    例如。 [Route] 元数据属性捕获托管远程服务的自定义路由,这是客户端需要知道的有关您的服务的必需信息,以便能够通过其发布的自定义路由调用服务。同样,IReturn&lt;T&gt; 接口标记为您的服务返回的内容提供了一个强类型合同,这就是启用ServiceStack succinct end-to-end Typed API 的原因。本质上,ServiceStack.Interfaces 是必需的扩展,以便能够在服务 DTO 中捕获整个服务合同。

    ServiceStack.Interfaces 可以在 ServiceStack 之外使用

    即使您不使用 ServiceStack,您仍然可以使用良性ServiceStack.Interfaces.dll,客户端可以自省以了解有关您的 DTO 和远程服务合同的更多信息。虽然我没有看到任何理由,但如果您想在项目中解耦 ServiceStack.Interfaces,您只需复制您在 DTO .dll 中使用的属性,将其从任何外部依赖项中释放出来。但这会影响您拥有通用服务客户端的能力,因为您的客户端库不知道这些嵌入式接口和属性,从而限制了使用它启用丰富通用功能的能力。

    其他语言的服务契约接口和属性

    为了支持TypeScript 等非 .NET 语言,ServiceStack 在生成的 DTO 中发出这些接口,因此它们不需要任何依赖项。

    同样,在添加 ServiceStack 参考 support of Swift 2.0Java and Android 中,这些额外的合同以惯用方式发出,引用 Java android 客户端包中的 Swift IReturn 协议或 IReturn&lt;T&gt; 接口,这也是启用简洁类型 API 的 ServiceStack 的原因在 iOS 和 Android 上。

    服务设计

    在设计 API 时,您应该牢记的是您的 Service Layer is your most important contract。也就是说,您的 API 的存在是为了让消费者可以访问您的远程服务器功能,因此您的内部逻辑应该是隐藏的 impl-detail,而不是应该影响 API 的外部表面积的东西。

    请求 DTO 定义了您的服务合同,我发现使用 Request 后缀是一个丑陋的结构,会对您的外部 API 的可读性产生负面影响,例如以下是带有*Request 后缀的名词的典型示例:

    var response = client.Get(new CustomerRequest { ... });
    

    与使用请求 DTO 具有指示性并提供更好的服务功能可读性的动词相比:

    var response = client.Get(new FindCustomers { ... });
    

    理想情况下,您的请求 DTO 应该是一个 动词,即 grouped by call semantics and Response Type。拥有*Dto 后缀表明您的内部实现正在泄漏并影响您的外部 API 使用者将绑定到的理想服务合同(并且永远不应更改)。请记住,您的 Service 的目标是为您的消费者提供可重用的功能,因此您的 impl 应该实现您发布的合约,而不是相反其实现规定合约应该是什么。

    考虑到这一点,我会将您的 ServiceStack 示例重写为:

    public class Authenticate : IReturn<AuthenticateResponse>
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    
    public class AuthenticateResponse
    {
       public AuthenticationResult Result { get; set; }
       public UserInfo UserInfo { get; set; }
    }
    

    这最终类似于 ServiceStack 的内置 AuthenticateAuthenticateResponse 请求和响应 DTO。

    我还建议阅读这个较早的答案以了解importance of DTO's and how it relates to the goals of a Service

    【讨论】:

    • 这个问题的答案很好,谢谢mythz。但我仍然对为什么不对 UserInfo 使用 *Dto 后缀感到困惑? IMO,AuthenticateResponse 中的 UserInfo 应该是 DTO 而不是域实体(例如:EF 中的 ORM 实体)。
    • 请求-响应 DTO 包含 ORM 实体? (我认为这不是一个好主意。)如果我错了,请纠正我。非常感谢。
    • @chansey 请阅读我在importance of DTOs and how OrmLite clean POCOs differ from Heavy ORMs 上的最后一个链接,最终做任何你觉得舒服的事情,但你应该有充分的理由对自己施加额外的限制、代码、摩擦和复杂性,即不要不要盲目地无缘无故地遵循一揽子规则,要确保你知道它们为什么会增加价值。
    • @chansey *Dto 不是已发布服务合约的理想名称,它是您最重要的模型,即您的消费者看到和绑定的唯一模型,并且应该是免费的并且与任何隐含问题解耦。 *Dto 是一个丑陋的后缀,表明它是您的内部实现的投影,而您的实现应该是您的服务合同的实现。您的外部 DTO 应该包含您的 impl 然后实现的理想名称。
    • @chansey DTO 是服务序列化的内容,它们应该是干净、良性的数据结构,其中没有任何逻辑,例如您将用于 OrmLite、Redis 等的数据模型。如果您没有使用 ServiceStack 库或 Micro ORM,那么不要重复使用您的数据模型并将它们复制到特定用途的 DTO 中——它们只是可序列化的 POCO。
    猜你喜欢
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多