Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic DTO mapping #21242

Open
1 task done
hillin opened this issue Nov 2, 2024 · 7 comments
Open
1 task done

Automatic DTO mapping #21242

hillin opened this issue Nov 2, 2024 · 7 comments

Comments

@hillin
Copy link

hillin commented Nov 2, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

In ABP we have to create AutoMapper mappings for a domain type and its corresponding DTO type. In multiple projects we developed, we found that most of the time we don't need to configure custom mapping rules for these types, they are basically one-to-one projections. To reduce boilerplate, we developed a method which automatically create these kind of mappings.

Describe the solution you'd like

The idea is to add an attribute DtoAttribute:

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class DtoAttribute(
    DtoMappingDirection mappingDirection = DtoMappingDirection.SourceToDto)
    : Attribute
{
    public DtoMappingDirection MappingDirection => mappingDirection;
    public string? SourceTypeName { get; set; }
}

public enum DtoMappingDirection
{
    SourceToDto,
    DtoToSource,
    Bidirectional
}

Any DTO class in the application contracts layer could be decorated with this attribute. In the application layer, automatically scan for types with this attribute in the contracts assembly, create mappings accordingly:

  • mappingDirection determines the mapping direction, as the enum suggests;
  • SourceTypeName could be specified or inferred by removing the Dto suffix of the DTO class. The source type is looked up from the domain assembly.

We can extend the rules a bit, such as if a type with [Dto] has a name ends with Input, automatically create a DTO-to-source mapping etc., but I'm not sure if it's necessary.

Additional context

I'd be happy to submit a PR for this feature.

@maliming
Copy link
Member

maliming commented Nov 4, 2024

hi

It seems the AutoMapper already has this feature.

https://docs.automapper.org/en/stable/Attribute-mapping.html

@hillin
Copy link
Author

hillin commented Nov 4, 2024

Yes, but it requires the contracts layer to reference the domain layer, which is an anti-pattern IMHO.

@maliming
Copy link
Member

maliming commented Nov 5, 2024

I think in this case we can refer to AutoMapper in the Contract module.

#1706

@hillin
Copy link
Author

hillin commented Nov 5, 2024

I'm not sure if we are on the same page, but what I meant was, to use AutoMapper's [AutoMap] attribute (in the contracts layer), you have to explicity specify the source type, which most of the time resides in the domain layer. However it is not desirable to reference the domain layer in the contracts layer, because it's against DDD's best practice.

My proposal was to keep the mapping in the application layer (rather than the contracts layer, despite adding an attribute into it), but in an automated manner. This way the contracts layer does not need to reference the domain layer.

@maliming
Copy link
Member

maliming commented Nov 6, 2024

hi

ABP has had this feature before. and it decide to remove it that's Because AutoMapper already provides it out of the box.

a48857f

@hillin
Copy link
Author

hillin commented Nov 6, 2024

@maliming yes, I'm fully aware of it, but I'm proposing a different approach.

I have the feeling that you have missed my point or I was not articulate enough, let me rephrase it:

AutoMapper's [AutoMap] attribute

AutoMapper allows us to decorate a class (in our case, the DTO class) with the [AutoMap] attribute to automatically register the mapping:

[AutoMap(typeof(MyEntity))]
public class MyEntityDto {}

There is one problem with this apporach: in the example above, MyEntityDto is defined in the ApplicationContracts project. By decorating it with the [AutoMap] attribute, we have to specify the source type, which is MyEntity, a type defined in the Domain project. This requires us to reference the Domain project in the ApplicationContracts project, which is against ABP's best practice, or any best practice of building layered application in general.

The feature ABP once had has the same problem.

The proposed approach

This approach does not require the ApplicationContracts project to reference the Domain project:

[Dto]
// or [Dto("MyEntity")] - optionally specify the name of the source type
public class MyEntityDto {}

The mapping was done by convention. If the decorated class' name ends with Dto, we automatically search for a class with the name that has the trailing Dto removed, in the domain project, to use as the source type (in the example above, MyEntity). The mapping is registered in the Application layer, so we don't have to violate any kind of best practice here.

@maliming
Copy link
Member

maliming commented Nov 7, 2024

@hikalkan What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants