假设我的服务层ServiceA
和ServiceB
中有两项服务,每项服务都有一个界面(分别为IServiceA
和IServiceB
)。
UI层仅引用从其方法返回 DTO 的服务接口。具体的服务类负责将域模型(EF POCO)映射到DTO。
ServiceA
通过依赖注入使用IoC容器依赖于IServiceB
,以便在该服务上调用某些方法。
这样做会产生一些问题:
与DTO之间的不必要/重复映射只是为了调用方法和/或使用结果。
将调用方法与调用方法输入参数和返回类型的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
}
}
答案 0 :(得分:2)
关于到DTO的不必要映射:考虑使用Data Access Objects或Repositories if you prefer Domain Driven Design来访问数据库。因此,您可以在服务层下面使用一种“实用程序层”,直接使用映射(实体)对象。
关于耦合的类型:ServiceB
可以实现多个接口,尤其是只在服务器端可见的接口。 ServiceA
可能依赖于该接口来访问不适合向客户端发布的ServiceB
的更多内部部分。
答案 1 :(得分:2)
我们基本上最终有两种选择来处理我们的情景。
将现有服务层拆分为两个独立的层:
仅处理域模型并允许进行服务间调用而无需dto映射的业务逻辑层。
“消息/服务”层,全权负责按摩业务逻辑层中的数据,以供客户使用。
根据@oddparity的建议,为每项服务提供公共和另一个内部接口。实现的公共接口方法调用内部方法。
我们选择使用选项2作为创建另一层抽象似乎是许多额外的开发人员工作,特别是当只有某些服务需要服务间调用时。
因此,我们只需为那些需要它们的服务创建内部接口。
这个article概述了分层架构,与我们的解决方案非常相似。
答案 2 :(得分:0)
如果我理解正确,可以通过将域对象而不是DTO传递到您的服务来解决这两个问题。通过这种方式,您可以避免不必要的映射,如果由于任何原因您必须更改应用程序接口/合同,您的服务可能保持不变。
恕我直言,DTO域映射应该只在您的应用程序的边界发生。例如,DTO到域映射应该是你的[控制器动作|的第一件事事件处理程序] do和域到DTO映射应该是返回结果之前的最后一个。
希望它有所帮助。