DTO';s和服务之间的呼叫

本文关键字:服务 之间 呼叫 DTO | 更新日期: 2023-09-27 18:25:35

假设我的服务层中有两个服务,ServiceAServiceB,每个服务都有一个接口(分别为IServiceAIServiceB)。

UI层仅引用从其方法返回DTO的服务接口。具体的服务类负责将域模型(EFPOCO)映射到DTO。

ServiceA通过使用IoC容器的依赖注入对IServiceB进行依赖,以便调用该服务上的一些方法。

这样做会出现几个问题:

  1. 与DTO之间的不必要/重复映射只是为了调用方法和/或使用结果。

  2. 将调用方法与被调用方法的DTO契约紧密耦合,输入参数和返回类型。

最初,我想将逻辑重构为一个内部方法,并从两个服务中调用它。然而,由于ServiceA依赖于接口IServiceB,因此不会公开内部方法。

你将如何处理这个问题?

进一步信息 (根据要求添加示例代码):

// This is the domain model
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}
// This is a dto for the domain model
public class CustomerDto
{
    public string Name { get; set; }
}
// Interface for ServiceA
public interface IServiceA
{
    void AddCustomer();
}
// ServiceA
public class ServiceA : IServiceA
{
    private readonly IServiceB _serviceB;
    // ServiceA takes in an IServiceB as a dependency
    public ServiceA(IServiceB serviceB)
    {
        _serviceB = serviceB;
    }
    public void AddCustomer()
    {
        var entity = new Customer();
        // !! This is the key part !!
        // I have to map to a dto in order to call the method on ServiceB.
        // This is a VERY simple example but this unnecessary mapping 
        // keeps cropping up throughout the service layer whenever
        // I want to make calls between services.
        var dto = Mapper.CreateFrom<CustomerDto>(entity);
        _serviceB.DoSomethingElseWithACustomer(dto);
    }
}
// Interface for ServiceB
public interface IServiceB
{
    void DoSomethingElseWithACustomer(CustomerDto customer);
}
// ServiceB
public class ServiceB : IServiceB
{
    public void DoSomethingElseWithACustomer(CustomerDto customer)
    {
        // Some logic here
    }
}

DTO';s和服务之间的呼叫

关于到DTO的不必要映射:如果您更喜欢域驱动设计来访问数据库,请考虑使用数据访问对象或存储库。因此,您可以在服务层下面有一种"实用程序层",直接处理映射的(实体)对象。

关于耦合类型:ServiceB可以实现多个接口,尤其是只在服务器端可见的接口。CCD_ 10可以依赖于该接口来访问CCD_ 11的更多不适合发布到客户端的内部部分。

我们最终基本上有两种选择来处理我们的场景。

  1. 将我们现有的服务层拆分为两个独立的层:

    • 业务逻辑层,只处理域模型,并允许服务间调用,而不需要数据到映射。

    • 一个"消息传递/服务"层,其唯一职责是从业务逻辑层对数据进行处理,为客户端使用做好准备。

  2. 正如@oddparity所建议的,为每个服务提供一个公共接口和另一个内部接口。实现的公共接口方法调用内部方法。

我们选择了选项2,因为创建另一个抽象层似乎需要大量额外的开发人员工作,尤其是当只有某些服务需要服务间调用时。

因此,我们只需为那些需要它们的服务创建内部接口。

本文对分层体系结构进行了很好的概述,它与我们的解决方案非常相似。

如果我理解正确,可以通过将域对象而不是DTO传递给您的服务来解决这两个问题。通过这种方式,您可以避免不必要的映射,并且如果出于任何原因必须更改应用程序接口/约定,您的服务可以保持不变。

IMHO、DTO域映射应该只发生在应用程序的边界中。例如,DTO到域的映射应该是[controller action|event handler]所做的第一件事,域到DTO映射应该是返回结果之前的最后一件事。

希望能有所帮助。