I'm starting to develop a Blazor Server Side application and my idea is to use :
- the Features folders structure (see https://medium.com/c2-group/simplifying-projects-with-a-folder-by-feature-structure-3a13cff2d28c)
- the Vertical Slice architecture (see https://jimmybogard.com/vertical-slice-architecture/). In fact, I will use CQRS pattern using MediatR library.
My folders structure will look like this :
Features Feature 1 XXXView.razor : the view XXXView.razor.cs : the partial class XXXView.razor.queries.cs : contains the queries (ex : GetCustomer) used by the view XXXView.razor.commands.cs : contains the commands (ex : SaveCustomer) used by the view Feature 2My question is the following : in this kind of architecture, is there a good reason to use a ViewModel (as in asp .net MVC projects) instead of using the entity directly in the view ?The code below is an example of what I would do without using ViewModel.
public partial class UserEditView : EditableComponentBase{ [Parameter] public long? Id { get; set; } [Required] public string Name { get; set; } protected override async Task OnInitializedAsync() { if (this.Id.HasValue) { UserEntity user = await Mediator.Send(new GetUserQuery(this.Id.Value)); this.Name = user.Name; this.Title = $"Change info of {user.Name}"; } else { this.Title = "New user"; } await base.OnInitializedAsync(); } protected override async Task Save() { UserEntity user = this.Id.HasValue ? await Mediator.Send(new GetUserEditViewQuery(this.Id.Value)) : new UserEntity(); user.Nom = this.Name; await Mediator.Send(new SaveUserCommand(user)); Close(); }}with GetUserEditViewQuery returning an entity with all information needed fetched or in lazy loading.
The code below is an example of what I would do if I use ViewModel.
public partial class UserEditView : EditableComponentBase{ [Parameter] public long? Id { get; set; } public UserViewModel User { get; set; } = new UserViewModel(); protected override async Task OnInitializedAsync() { if (this.Id.HasValue) { this.User = await Mediator.Send(new GetUserEditViewQuery(this.Id.Value)); this.Title = $"Change info of {this.User.Name}"; } else { this.Title = "New user"; } await base.OnInitializedAsync(); } protected override async Task Save() { await Mediator.Send(new SaveUserCommand(user)); Close(); }}with GetUserEditViewQuery returning a ViewModel containing all information needed.
As you can see, the differences if I didnt use the ViewModel is that the view :
- define the fields that will be displayed itself (without using a ViewModel)
- get the data using a query which return here an entity (but which could return something else if needed) then use it to set the variables
- convert the variables to an entity (or another object if needed) and call a command to save the information=> this seems really similar to how I would develop a desktop application
I would be interested by the pros and cons of each approach.
UPDATE@Henk Holterman : I change the GetUserQuery of the example 2 by GetUserEditViewQuery
@Ben Sampica : First, thanks for your answer.If I understand you correctly,In a use case "Change user information", the ViewModel (UserEditViewModel) will contain all information needed to edit the user.In a use case "View user information", the ViewModel (UserViewViewModel : not really nice but ...) will contain all information needed to view the user (which can differ from the change page).=> the queries are different and so return different ViewModel.
I understood and it makes sense.
But is there only one ViewModel by page or can I use more than one?Indeed, in some cases, pages contains list with a button which permits the user to add an element in this list.For me, adding an element in the list will call a new query which add the element in the database then the list is refreshed.
In lot of examples, I see people create a property in the ViewModel of the page (say UserEditViewModel) and populate it in the main query (say GetUserEditViewQuery) which is not really possible here.If I had to do it, I would certainly do something like this :
this.List = await Mediator.Send(new GetListForUserEditViewQuery());this.User = await Mediator.Send(new GetUserEditViewQuery());Would it be correct ?