2013-07-31 54 views
4

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

UI层仅引用服务接口,从其方法返回DTOs。具体的服务类负责将域模型(EF POCO)映射到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 
    } 
} 
+1

请问您可以添加一些代码(无需实际处理),以便更容易理解您的用例和当前体系结构? –

+0

根据要求添加示例。我试图将其降至最低限度,以突出显示实际问题案例之一。返回值等会出现同样的问题。 –

+0

我是否正确理解以这种方式您有两个生成的文件,其中定义了CustomerDto(我不计算您的DTO项目声明) - 在ServiceA和ServiceB的命名空间中,尽管存在此声明是平等的 - 他们是不同的实体。你正在寻找一种解决方法吗? –

回答

2

关于unncessary映射到DTO的:考虑使用Data Access ObjectsRepositories if you prefer Domain Driven Design来访问数据库。因此,您可以在服务层下面直接使用映射(实体)对象的“实用程序层”。

关于耦合类型:ServiceB可以实现多个接口,特别是只在服务器端可见的接口。 ServiceA可能依赖于该接口访问ServiceB的更多内部部分,这些部分不适合发布到客户端。

+0

我已更新我的问题以包含示例代码。我正在使用存储库/工作模式单元。然而,调用其他服务后面的想法是,我可能会执行一些其他业务逻辑。我不想把这个逻辑放在数据访问层中。尽管我喜欢你对多个接口的建议。 –

0

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

恕我直言,DTO域映射应该只发生在你的应用程序的边界。例如,DTO到域映射应该是你的第一件事[控制器动作|事件处理程序] do和域到DTO映射应该是返回结果之前的最后一个。

希望它有帮助。

+0

在我的应用程序中,我的服务/ BLL层是应用程序的边界。这是一个“API”,由多个客户甚至第三方使用。正如[this](http://msdn.microsoft.com/zh-cn/magazine/ee236638.aspx)文章指出的,将您的域模型暴露在您的服务层之外会在您的服务和客户端之间形成紧密的耦合,而我不要。 DTO实质上是每种服务方法的数据合同,并且可以更灵活地进行更改。 –

+0

我假设您在应用程序服务和应用程序客户端之间有一个额外的层。是的,我同意不公开该模型。 我不知道你的项目的限制,但如果可能的话,我会添加额外的图层。这是执行实体映射,输入验证的好地方,它也将允许您(至少在这种特殊情况下)打破您的服务之间的依赖关系。 – xdarsie

2

我们基本结束了两个选择来处理我们的场景。

  1. 打出了我们现有的服务层分为两个独立的层:

    • 业务逻辑层,只有与领域模型处理,并允许无需DTO映射跨服电话。

    • “消息传递/服务”层,唯一负责按照业务逻辑层中的数据进行消费。

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

我们选择使用选项二去,创建抽象的又一层好像很多额外的工作,开发商,尤其是当只有某些服务所需要的跨服电话。

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

article给出了一个很好的分层体系结构的概述,并与我们的解决方案非常相似。