DTO and calls between services

Let's say I have two services on my service, ServiceA and ServiceB , each of which has an interface ( IServiceA and IServiceB respectively).

The user interface layer contains links to service interfaces that return DTOs from their methods. Specific service classes are responsible for mapping domain models (EF POCOs) to DTOs.

ServiceA accepts a dependency on IServiceB through dependency injection, using the IoC container to invoke some methods of this service.

This raises several problems:

  • An unnecessary / duplicate mapping to and from the DTO only to invoke the method and / or use the result.

  • Tight binding of the invocation method with DTO contracts of the input parameters of the invoked methods and the type of the return value.

At first, I decided to reorganize the logic into an internal method and call it from both services. However, since ServiceA accepts a dependency on the IServiceB interface, internal methods are not displayed.

How would you deal with this problem?

Additional information (added sample code on request):

 // 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 } } 
+4
source share
3 answers

Regarding raw mapping to the DTO: consider using Data Access Objects or Warehouses if you prefer to create a Driven Design domain to access the database. Thus, you may have a kind of "service level" under your service level, working directly with displayed objects (objects).

As for the type of connection: ServiceB can implement more than one interface, especially one that is visible only on the server side. ServiceA may depend on this interface to access more internal parts of ServiceB that are not suitable for client-side publishing.

+2
source

Basically, we got two options for solving our scenario.

  • Divide our existing service layer into two separate layers:

    • A business logic level that applies only to the domain model and allows for interservice calls without the need for dto mapping.

    • The level of "messaging / services" with exclusive responsibility for massaging data from the level of business logic, ready for use by customers.

  • As suggested by @oddparity, specify both an open and a different backend for each service. Embedded open interface methods invoke internal methods.

We decided to go with option 2, since creating another layer of abstraction seemed like a lot of additional developer work, especially when some services required interservice calls.

As a result of this, we simply create internal interfaces for the services that they need.

This article provides an excellent overview of layered architecture and one that closely resembles our solution.

+2
source

If I understood correctly, both problems can be fixed by transferring to domain objects instead of DTO in your services. This way you can avoid unnecessary mappings, and your services can remain intact if for any reason you need to change the application interface / contract.

IMHO, DTO domain mappings should only occur within your application. For example, a DTO mapping for a domain should be the first thing your [controller action | event handler] do and the domain for DTO mapping must be the last before returning the result.

Hope this helps.

0
source

Source: https://habr.com/ru/post/1494489/


All Articles