jasontaylordev / CleanArchitecture

Clean Architecture Solution Template for ASP.NET Core
MIT License
16.92k stars 3.64k forks source link

Share DTOs and validation logic between different features. #122

Closed Dishanx closed 4 years ago

Dishanx commented 4 years ago

This is actually not a feature request. but a question.

CQRS design patten we build unique data models for reads and writes. and treat them as separate concerns with separate requirements.

When i start my project with CleanArchitecture template i notice i have to duplicate some of my Dto.I just want to make sure i'm doing it right.

In my project Author, Course and some of other controllers share same Dtos e.g : CreateCourseDto.cs(creating), CourseDto.cs(retreaving)

goal : I want to create author with initial list of courses. Each couse consists same attributes and validations that CourseController has. And Author, Student and Couse controllers use same CouseDto when it retreaving couse information(authors/1 list all the courses he teach,student/1 list all courses he enroll with). and also UpdateCouseDto and CreateCouseDto share CouseForMaipulationDto.

What i did was.

    public class CreateAuthorCommand : IRequest<Guid>
    {
        public string FirstName { get; set; }
        ....
        public ICollection<AuthorCourse> CoursesforAuthor { get; set; } = new List<AuthorCourse>();
    }
    Public class AuthorCourse
    {
        public string Title { get; set; }
        public string Description { get; set; }
    }

CreateAuthorHandler add Author to database CreateCouseHandler add Couse to database

//AuthorsController.cs

        public async Task<ActionResult<AuthorDetailDto>> CreateAuthor(CreateAuthorCommand createAuthorCommand)
        {
            var result = await Mediator.Send(createAuthorCommand);
            if (result != Guid.Empty)
                foreach (var authorCouse in createAuthorCommand.CoursesforAuthor)
                {
                    await Mediator.Send(new CreateCourseCommand()
                    {
                        AuthorId = result,
                        Description = authorCouse.Description,
                        Title = authorCouse.Title
                    });
                }
            return CreatedAtRoute(nameof(Get), new { authorId = result }, result);
        }

When reatreaving i created separate DTOs for different feature like CourseForStudentDto.cs, CourseForAuthorDto.cs etc...

I want to make sure i'm doing it right and i put each dto's (AuthorCouse) in correct place. Is there any better alternative?

justinjstark commented 4 years ago

I use different DTOs across features for queries. They will probably diverge and there is little benefit from sharing them (but there are definitely exceptions to this).

On writes, it's different. If you create an author with initial courses, you want to use the same validation logic for creating a course as when you create an author with a course. That's a good example of when I'd want to share my commands and command validations.

One thing I would change about your example is taking all the logic for CreateAuthor out into its own handler (CreateAuthorWithCourses or such). Then you can nest your CreateAuthor and CreateCourse handlers within it. Some people turn up their noses at nesting handlers but I find it can be useful.