How to mutate editmodel/postmodel to domain model
Asked Answered
I

4

9

In an ASP.NET MVC project we are using AutoMapper to map from domain model to viewmodel - and sometimes also flattening a hierarchy while doing so. This works like a charm and makes the rendering logic of our views very lean and simple.

The confusion starts when we want to go the other way from viewmodel (or postmodel or editmodel) to domain model, especially when updating objects. We can't use automated/two-way mapping because:

  1. we would have to unflat the flattened hierarchy
  2. all properties on the domain model would have to be mutable/have public setters
  3. the changes coming from the view isn't always just flat properties being mapped back to the domain, but sometimes need to call methods like "ChangeManagerForEmployee()" or similar.

This is also described in Jimmy Bogards article: The case for two-way mapping in AutoMapper, but the solution to this isn't described in detail, only that they go:

From EditModel to CommandMessages – going from the loosely-typed EditModel to strongly-typed, broken out messages. A single EditModel might generate a half-dozen messages.

In a similar SO question there is an answer by Mark Seeman where he mentions that

We use abstract mappers and services to map a PostModel to a Domain Object

but the details - the conceptual and technical implementation - is left out.

Our idea right now is to:

  1. Recieve a FormCollection in the controller's action method
  2. Get the original domain model and flatten it to viewModelOriginal and viewModelUpdated
  3. Merging the FormCollection into viewModelUpdated using UpdateModel()
  4. Use some generic helper method to compare viewModelOriginal with viewModelUpdated
  5. Either A) Generate CommandMessages a la Jimmy Bogard or B) Mutate the differences directly into the domain model through properties and methods (maybe mapping the 1-1 properties directly through AutoMapper)

Can someone provide some examples of how they come from FormCollection through editmodel/postmodel to domain model? "CommandMessages" or "abstract mappers and services"?

Inhale answered 3/7, 2012 at 15:5 Comment(3)
I'd give this question a thousand upvotes if I could. I've been searching endlessly to a solid answer to this.Bratislava
What process did you go with in the end?Lengthen
Hi Tom. We ended up using a mix and match solution, where we 1. Retrieve the Dom object from DAL/DB, 2. flatten Dom object to ViewModel, 3. use TryUpdateModel with FormColl on ViewModel, 4. use AutoMapper to do a reverse mapping from the flattened viewmodel to the Dom (Hierarchy) on simple properties and 5. use a service to map non-trivial properties from viewmodel to Dom methods (Dom object and/or Dom Service). Hopes this helps - I'm not sure which answer to mark as correct, since it's kinda mix of more than one...?Inhale
P
2

I use the following pattern:

[HttpPost]
public ActionResult Update(UpdateProductViewModel viewModel)
{
    // fetch the domain model that we want to update
    Product product = repository.Get(viewModel.Id);

    // Use AutoMapper to update only the properties of this domain model
    // that are also part of the view model and leave the other properties unchanged
    AutoMapper.Map<UpdateProductViewModel, Product>(viewModel, product);

    // Pass the domain model with updated properties to the DAL
    repository.Update(product);

    return RedirectToAction("Success");
}
Peyter answered 3/7, 2012 at 15:23 Comment(2)
Hi Darin, thanks for your answer, but I'm looking for how to mutate/translate the flattened viewmodel back into the domain model hierarchy/aggregate not only through mapping/properties but also using methods.Inhale
Seems that this pattern would result in unclear mapping to domain object.Uptrend
S
1

You might want to consider CQRS(Command Query Responsibility Segregation - I think this might be the concept you were missing), possibly even with Event Sourcing.

It is basically a practice of separating the logic of reading from a data source and writing to a data source, might even mean having different data models for reading and writing.

This might be a good place to start: http://abdullin.com/cqrs/

Sustain answered 3/7, 2012 at 23:28 Comment(10)
Hi Pawel, thx I've been looking at CGRS earlier, maybe I'll look into it again. I already have some ideas on how to seperate logic, but I'm specifically looking on how to translate the FormCollection/EditModel to Domain Model maybe through command messages, but how do I know which messages to create? Or if doing services (abstract mappers) how do I know what has changed? My EditModel only contains the result of the changes, not the changes/messages themselves.Inhale
How I understand your situation is: In UI you have a big form that has, say 20 fields and values are entered but you don't know which ones as you send all of the values back to server (after user pressed "Save"). How I would approach it, trying to achieve command/query separation, is I'd split the "Save" action into commands (commands come from UI). In practice it would mean sending back only the fields that have been altered. And I don't think I'd bind the values back to the view model but treat them simply as a list of commands.Homeomorphism
That's right, I have a big edit form that is filled in with the flattened view model. When the user edits and post the form back to the controller I can't see which fields has been changed. My idea is to compare a set of "Before" and "After" viewmodels (the original that was filled in and an updated which is the original+merged form post values trough UpdateModel) and then generate commands from there (server-side). So I guess it depends on what you mean by "UI": UI as in client-side JS or UI as in server-side view+controller?Inhale
By UI I meant the client-side code. I know it might seem weird but I think it's because it means changing the approach to the way the whole application is designed which is the point of CQRS. I am currently in the process of transforming a mature web application to CQRS and it requires a bit of effort in the beginning. But the necessity has risen from performance and scalability issues and even though it is currently a sort of a hybrid (as it needs to be constantly operational so I'm making changes bit by bit) the change of approach already has proven very promising.Homeomorphism
Back to the point. One way to go with the commands in the UI would be to make the form post changes as they are made in each field with AJAX. So when the user changes Lastname, js sends a command UserLastnameChanged to the server side. And so on. The UI would not be waiting for the user to press "Save". But I think you want the changes to be transactional. That's why in your case you should just mark the "dirty" fields in js and after the user has pressed "Save" only send the ones marked.Homeomorphism
Others have suggested comparing models from "before" and "after" on the server side but I'd oppose that solution for a few reasons: 1. It might become complex and hard to maintain 2. On the client-side you already have the information about what has been changed "for free" 3. It might require introducing state management which again makes the solution more complex, less elegant and isn't RESTful.Homeomorphism
Really good points, Pawel! It seems to me I should be looking at CQRS further down the road, but right now that will be to big a mouthful for us. One of the reasons being that we use 3rd party controls on the client side (Telerik/KendoUI) to render grids and edit forms and that they don't support creating messages, but sends the updated editmodel(s) to the server through regular form posts. We would then have to build an extra js framework on top of these controls to record/translate changes into command messages, which is too much at the moment.Inhale
Pawel, regarding the "before" and "after" comparison, I think you're right regarding the complexity of this. Maybe it will be better to have a handler/service that maps the simple 1:1 properties directly back to the domainmodel (using AutoMapper with a specific map for this = testable) and then handcode the special mutations/translations as in If editModel.CurrentManagerId <> domainModel.CurrentManager.Id Then domainModel.ChangeManager(newManager) End If. My Repository/UnitOfWork (linq2sql) will do the change tracking and only send updates to the DB if something was actually changed.Inhale
Sounds like what Darin suggested then :)Homeomorphism
Well kinda... Except that it's not just the simple 1:1 properties mapping I'm looking for, which is fine for the tedious stuff, but in some cases I also might need to "unflatten" my editmodel back into multiple domainmodels (unless it's an aggregate) and handle methods calls as in ChangeManager(). I was curious as how to do this, e.g. how Jimmy Bogard and Mark Seeman does it. But I guess it's a mix-and-match between automated mapping and handcoded method calls (if we forget about comparing "before" and "after").Inhale
P
0

Option C: Put it all in the controller action. Next, if that gets hairy, decompose into services (abstract mappers) or messages-as-methods (the command message way).

Command message way:

public ActionResult Save(FooSaveModel model) {
    MessageBroker.Process(model);

    return RedirectToAction("List");
}

And the processor:

public class FooSaveModelProcessor : IMessageHandler<FooSaveModel> {

    public void Process(FooSaveModel message) {
        // Message handling logic here
    }

}

This is really just about moving the "processing" of the form out of the controller action and into individual, specialized handlers.

But, I'd only really go this route if controller actions get hairy. Otherwise, just take the form and do the appropriate updates against the domain models as necessary.

Palua answered 4/7, 2012 at 4:1 Comment(4)
Hi Jimmy, thx for your answer. I'm definitely trying not to get things to hairy and keeping my controllers lightweight as in ASP.NET MVC 4 in Action. I've also been looking at command messages (e.g here and "Using an application bus" in the book), but how do you know what has changed in your FooSaveModel? In the old days of webforms I would have a SelectedIndexChanged event from my CurrentManager-dropdown. Now I only have the end result in FooSaveModel?Inhale
Jimmy, would your implementation of Process(FooSaveModel message) be similar to the suggestion I made to Pawel? That is, you don't actually look at "before" and "after" savemodels to see what has changed, but instead compare and map the SaveModel directly to the DomainModel, optionally calling methods as needed (e.g. ChangeManager())?Inhale
Why would you need to care what has changed or not? If I care about specific fields being changed, that could indicate that I need more screens with task-specific interfaces (and validation).Palua
For instance, I would need to know that editModel.CurrentManagerId has changed as to whether I should call domainModel.ChangeManager() or not. It also seems a bit redundant to map all of my flat 1:1 editmodel properties back to the domainmodel(s), when maybe none of them has actually changed? It seems I'm putting a lot of work and change tracking responsibility on my repository/UnitOfWork, when my UI should be able to figure it out?Inhale
L
0

There are some similarities here with what I've been doing. My hierarchy of view models is only somewhat flattened from its domain object equivalents, but I do have to deal with calling explicit service methods on save to do things like adding to child collections, changing important values etc, rather than simply reverse mapping. I also have to compare before and after snapshots.

My save is Ajax posted as JSON to an MVC action and enters that action magically bound back to a view model structure by MVC. I then use AutoMapper to transform the top level view model and its descendants back into its equivalent domain structure. I have defined a number of custom AutoMapper ITypeConverters for those cases where a new child item has been added on the client (I'm using Knockout.js) and I need to call an explicit service method. Something like:

            foreach (ChildViewModel childVM in viewModel.Children)
        {
            ChildDomainObject childDO = domainObject.Children.Where(cdo => cdo.ID.Equals(childVM.ID))).SingleOrDefault();
            if (childDO != null)
            {
                Mapper.Map<ChildViewModel, ChildDomainObject>(childVM, childDO);
            }
            else
            {
                MyService.CreateChildDO(someData, domainObject); // Supplying parent
            }
        }

I do a similar thing for deletes and this process cascades quite nicely down through the whole structure. I guess a flattened structure could be either easier to work with or harder - I have an AbstractDomainViewModel with an ID with which I do the above matching, which helps.

I need to do comparisons before and after updating because my service layer calls trigger validation which can affect other parts of the object graph, and this dictates what JSON I need to return as the Ajax response. I only care about changes which are relevant to the UI, so I transform the saved domain object back to a new view model and then have a helper method to compare the 2 view models, using a combination of manual upfront checks and reflection.

Lengthen answered 6/7, 2012 at 5:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.