Currently I'm trying to migrate legacy application to some api's using Clean Architecture. Until now I was able to go through changes, but every time I encounter a DTO I cannot understand how to place it in the clean architecture. By DTO, I am referring to: Object containing multiple properties from domain entities combined. I'm using DTO's because the database is still in "legacy format" but the api must expose diferent formats of responses across multiple systems.
Let's say I have the following structure:
Domain:
public class EntityA
{
public string Prop1{get; set;}
}
public class EntityB
{
public string Prop2{get; set;}
}
Then I have a interface to a Service as follow:
public interface IService
{
}
In the application layer (Use Cases) I have the implementation of the services described in the Domain and the DTO itself:
public class DTO
{
public string Prop1{get; set;}
public string Prop2{get; set;}
}
public class Service : IService
{
public IEnumerable<DTO> RetrieveDto()
{
return new DTO()//Construct DTO....
}
}
And here my issue is starting.
I need to modify the domain service interface to return the DTO. This is generating a circular reference and I don't think is ok to be done.
I tried to create an abstract DTO class in the domain and inherit from it to avoid the reference from Domain to Application. But I'm not pretty sure this should be a solution because DTO's are just object that store data, I don't have anything in that abstract class.
Currently, the mapper and the DTO are placed in the Application because from the application I access the Infrastructure for repositories and here is where I map the entity to a DTO.
So my question is: Do I understand something wrong here? Where should be DTO places correctly?
Thank you!
CodePudding user response:
- Most popular architecture style : Layer 1 -Entity (Entities/Models) => You can keep dto's here in a DTO'S folder -Data Access (You retrieve data) => You can map dto's here with entities here -Business => Repositories , Services -Core => Helper methods common things -API => Controllers,Communication with fronted aka client
CodePudding user response:
I think it is not accurate that you see the DTO so separate from the entities. After all, if your use case needs to return this data structure, it belongs to / under the use case.
Side note: I also dislike the term "dto" as this does not specify anything out of the ordinary. (Nearly all objects contain data and are transferred) But on to your use case: I would rename the DTO to "UseCaseXResponse" and then put it next to the other entities. All the entites would then consist of some input-oriented ones, some output-oriented ones and maybe also some general purpose ones. The logic, how to convert the input ones to the output ones is in the use case class.
If you feel that this data agglomeration has no place in your business logic, then you need to expose other entites to the outer layer and use that outer layer to aggregate the response into a dto.
CodePudding user response:
You must define the DTO in the domain layer, along with the service's interface. The DTO is essentially part of the service's interface definition and it doesn't make any sense without it.
When you think about implementations of that service, in outer layers, those implementations would all share the ability to return that DTO type, despite different internal implementations.
The domain layer methods that depend on the service's defined interface also depend on the definded DTO as the service's method(s) return type.
Something like:
public class Domain.DTO
{
public string Prop1{get; set;}
public string Prop2{get; set;}
}
public interface Domain.IService
{
DTO DoSomething();
}
public class Domain.EntityA
{
public string Prop1{get; set;}
// Super-contrived, but you get the point...
public void DoSomethingWith(IService service)
{
// regardless of the actual implementation,
// I know result will always be DTO-shaped
var result = service.DoSomething();
}
}
public class Application.ServiceOne : Domain.IService
{
public Domain.DTO DoSomething()
{
// Do something one way, but providing the agreed DTO
}
}
public class Application.ServiceTwo : Domain.IService
{
public Domain.DTO DoSomething()
{
// Do something another way, but still providing the agreed DTO
}
}
This maintains all dependencies travelling inwards as promoted by the architecture.