Working sample available at: https://gitlab.com/nipunrd_git/mvvmarch
I am in process of migrating an app from MVC to MVVM. As far as I have read, you need to make your viewController independent of the Model, there by making a viewModel to glue the model and the controller together.
If we consider a simple screenA which has a refresh button to get some data via a web service and then fill the data in the view:
ScreenAViewController.swift: Will contain the code to hit the web service via a singleton network helper class.
ScreenA -> NetworkHandler -> fetchData
In order to make ScreenA, use the fetched data, we should now make a ViewModel, so I made ScreenAViewModel. Let's say our model was named "Design":
struct Design{
var pattern: String
}
Now our ViewModel would look like this:
class ScreenAViewModel{
private let design: Design
public let pattern: String
init (design: Design, pattern: String){
self.design = design
self.pattern = design.pattern
}
}
When we get the data from the web service, we should first create a Design model and then initialize our ViewModel with that. This ViewModel will now be used by our view controller to fetch and display data from. Any Business logic will be written in this only.
Is there a way to shift #3 to the view model itself? I think by having this conversion from model to viewModel in the controller itself is beating the purpose of hiding model from controller.
Related
I have a class that takes care of all network request. So my confusion is where should i call this network request class either from viewcontroller or model class. Suppose i have 'User' model class with properties id,name,phone. In order to access login API, should i create a function 'loginwithphone' within this model class and call it from viewcontroller or should i call network request class directly from Viewcontroller. Is the network request class considered as a model in itself? If so, calling it from viewcontroller will still make my code architecture as MVC. Doesnt it?
You should take a look to Observer pattern which defines MVC architecture, to define any way to manage your request through callbacks.
Model layer of MVC architecture defines your POJOS, so 'loginwithphone' function should be in a controller class, for example 'logincontroller'.
To sum up, your network request class should be considered as a class inside a resources layer. and should call your controller class.
Hope it helps!
In MVC you should call the network request through a networking class. Preferably you will first run a class or enum that will build the request for you. After that you will get the model object returned to you that you will have stored in your viewController and send to all the views you want to populate.
Off-topic: I like MVVM as you then call another class (viewModel) which holds all the models and the requests, and all you have in your viewController are views and callbacks waiting for the viewModel to finish it's requests.
Example:
//ViewController
self.viewModel.fetchData()
self.viewModel.dataFetched = {
view.populate(withData: self.viewModel.datas)
}
//ViewModel
var dataFetched: (() -> ())?
var datas: [Data] = []
func fetchData() {
let request = networkManager.buildRequest()
sendRequest( success: { datas in
self.datas = datas
dataFetched?()
})
}
This is a very vague description of how its done. But i like this way
Here is the scenario, I need to load up a view model object from a several domain objects that are being returned from multiple web service service calls. The code that transforms the domain model objects into the digestible view model object is a bit of fairly complex code. The three places that I have thought about putting it are:
Internal methods inside of the controller to load up an instance view model.
Static get method or property on the view model class itself that returns a loaded instance of view model.
An altogether separate builder or helper class that has a Static get method, property, or overloaded constructor that returns the loaded instance of view model.
To be clear, I don't want to use AutoMapper, or tools like it. From a best practices standpoint I'd like to know where this logic should go, and why.
EDIT
So here is what I have so far, this does give me "skinny" controller logic and separation of concerns. How can I make it better though?
// ** Controller **
public ActionResult Default()
{
var viewModel = MyViewModelBuilder.BuildViewModel(MarketType.Spot);
return View(SpotViewUrl, viewModel);
}
// ** Builder **
// Lives in MVC project under ViewModelBuilders folder
public class MyViewModelBuilder
{
public static ChartsModel BuildViewModel(MarketType rateMarket)
{
var result = new ChartsModel
{
RateMarket = rateMarket,
DateRange = new DateRange()
};
LoadGroupedRateLists(result, rateMarket);
LoadCoorespondingRates(result);
return result;
}
private static void LoadGroupedRateLists(ChartsModel model, RateMarket rateMarket)
{
var rateHistSvc = new RateHistoryService(RatesPrincipal.Current.Session.Token);
var serviceResult = (rateMarket == RateMarket.Spot)
? rateHistSvc.GetSpotNationalRateHistory()
: rateHistSvc.GetContractNationalRateHistory();
// Break lists apart by category, and re-sort and trim.
model.Cat1Rates = CategorizeTrimAndSort("cat1", false, serviceResult);
model.Cat2Rates = CategorizeTrimAndSort("cat2", true, serviceResult);
model.Cat3Rates = CategorizeTrimAndSort("cat3", false, serviceResult);
model.Cat4Rates = CategorizeTrimAndSort("cat4", true, serviceResult);
model.Cat5Rates = CategorizeTrimAndSort("cat5", false, serviceResult);
model.Cat6Rates = CategorizeTrimAndSort("cat6", true, serviceResult);
// Get Date range from results.
var sortedRateMonths = serviceResultNational
.Select(rate => rate.YearMonth)
.OrderBy(ym => ym.Year)
.ThenBy(ym => ym.Month);
model.DateRange.Start = sortedRateMonths.First();
model.DateRange.End = sortedRateMonths.Last();
}
...
}
1 or 3, not 2. Provided that if you do #3, you do not actually let the static method do the web service calls, just let it do the mapping. Domain object(s) in, viewmodel(s) out. Prefer extension method to overloaded constructor, if the object doesn't need to track state, there's no benefit to making it non-static.
Why?
As soon as you put a logic method on the model, it ceases to be a POCO. Best practice is to treat viewmodels like boring data buckets as much as possible. Some people also try to do the mapping in a viewmodel constructor which is not a good idea once you get into any kind of complexity in the mapping.
If you only need to do the mapping in one controller, you can put it in a sub-routine. Keep in mind if you want to test the sub in isolation and keep it internal, your project will have to have InternalsVisibleTo your test project.
Update
After looking at your code, I am kind of inclined to agree with #C Sharper that this belongs neither in the controller, viewmodel, nor helper class / method. Composing this ChartsModel is very interesting code, and contains a lot of business logic. It really should be in a separate layer. Your controller should call down into that layer and delegate all of this interesting and important code to another abstraction. That abstraction should then return a domain object, as #C Sharper said. Whether you use that domain object as your viewmodel, or DTO it into a different viewmodel, is up to you. Here's how something like that might look like:
public class MyController : Controller
{
private readonly IComposeChartData _chartDataComposer;
public MyController(IComposeChartData chartDataComposer)
{
_chartDataComposer = chartDataComposer;
}
public ActionResult Default()
{
var chartComposition = new ChartCompositionSettings
{
MarketType = MarketType.Spot,
Token = RatesPrincipal.Current.Session.Token,
};
var chartData = _chartDataComposer.ComposeChartData(chartComposition);
var chartModel = Mapper.Map<ChartsModel>(chartData);
return View(SpotViewUrl, chartModel);
}
}
That is a nice lean controller body. The abstraction might then look something like this:
public class ChartDataComposer : IComposeChartData
{
public ChartData ComposeChartData(ChartCompositionSettings settings)
{
// all of the interesting code goes here
}
}
This way, your viewmodel does not need to move out into a separate layer, but you do need to create a similar object (ChartData) in that layer. The interface decouples your controller from the data it needs, and the object it returns is cohesive with your presentation data (viewmodel).
I guess I don't really see this code as business logic, but more as
presentation logic. Why do you see it as business logic?
Think of your RateHistoryService class as a supplier. You receive raw materials from it, and transform those raw materials into something different, creating value in the process. This is what businesses do.
In this case, the chart visualizations are the value you are providing. Otherwise, your customers would have to sift through the raw data, trim it, categorize it, sort it, group it, etc., themselves before they could create similar charts.
I probably should have explained this earlier, but the service calls
are to our own business layer already, and return domain layer
business objects. It seems weird to me to have more than one business
layer.
Your business layer can have its own internal layering. In this case, you can create a RateChartingService that uses the RateHistoryService to return a RateChartingResult busines object. Then map that to a ChartsModel (or like I said before, use it directly as your viewmodel).
I would say don't do it in your Controller. Controllers should be as "skinny" as possible. I would do it like this.
Your "Data Layer" would assign the Domain objects its properties and values.
Then your subsequent Layer call it "Business Layer" will transfer your Domain Object into your ViewModel. And you will simply pass the view model to your controller without having the Controller handle any of that logic.
Separation is very important. Domain Objects should stay out of the controller, and the controllers should only care about View Models.
I am new to MVC and am having a conceptual problem with state and object persistence and hope someone can put my thoughts in order.
I have a remote webservice which provides methods to manage orders. An order consists of a header and Lines as you would expect. Lines can have additional requirements.
I have my domain objects created (using xsd2code from the webservice schema), the webservice calls and object serialization all working fine. I've build the DAL/BLL layers and it's all working - tested using a WinForms testbed app front-end.
I have view model objects mapped from the domain objects using Automapper. As the order is returned from a single webservice method complete with lines etc I have an OrderViewModel as follows
public class OrderViewModel
{
public OrderHeaderViewModel OrderHeader { set; get; }
public List<OrderLineViewModel> OrderLines { set; get; }
public List<OrderLineAdditionalViewModel> OrderLineAdditional { set; get; }
public List<OrderJustificationViewModel> OrderJustifications { set; get; }
}
Firstly I'm wondering if I should dispense with the OrderViewModel as if I pass this as a model to a view I'm passing far more data than I need. Views only need OrderHeader or OrderLines etc - not the entire order.
Now my conceptual problem is in the controllers and the views and object persistence.
My Order controller has a Detail Action which performs the load of the order from the webservice and maps the Domain object to the OrderViewModel object.
public ActionResult Details(string orderNumber)
{
OrderViewModel viewModel = new OrderViewModel();
var order = WebServiceAccess.LoadOrderByOrderNumber(orderNumber,"OBOS_WS");
viewModel = AutoMapper.Mapper.Map<BusinessEntities.Order, ViewModels.OrderViewModel>(order);
return View(viewModel);
}
But the Order/Details.cshtml just has the page layout and a call to two partial pages for the header and the lines (I swap the Headerview for a HeaderEdit using Ajax, same for the LinesView)
#{ Html.RenderPartial("DetailsHeaderViewPartial", Model);}
#{ Html.RenderPartial("DetailsLinesViewPartial", Model);}
At the moment I'm passing the model into the main Details container page, then into the RenderPartials, However I don't think that the model should be passed to the main Detail page, as it doesn't need it - the model is only needed in the DetailsHeaderViewPartial, DetailsLinesViewPartial so I'd be better off using #RenderAction here instead and passing the model into the Header/Lines views instead.
However, The Order is retrieved from the webservice in the ActionResult Details() how can I make the retrieved OrderViewModel object available in the ActionResult HeaderDetails() / LineDetails() methods of the controller to pass as the model in return PartialView(...,model) ?
Should I use a User Session to store the Order ViewModel so it can be used across actions in the controller.
Moving on from this stage the user will be able to maintain the order (add/remove lines - edit the header etc). As the webservice call to save the order could take a few seconds to complete I'd rather only call the save method when the user has finished with the order. I therefore would like to persist the in-progress order locally somewhere whilst it's being worked on. User session ?
Many thanks for any advice. Once I've got my head around state management for the ViewModels I'll be able to stop reading a million Blog posts and actually write this thing !
You actually have a few questions here so I will try to address them all the best I can.
1) Dispensing with the view model : I would say no. The view model represents the data that you need in order to populate your view. It seems like you are using the view model as an identical container to the domain model object. So you are asking if you should dispense with it and just pass the domain model to the view while your original concern is that you are passing along more data then you really need as is?
Rather then dispensing with the view model, I would revisit your properties on your view model. Only use properties that you need and create the mapping logic (either with automapper or on your own) for taking the complex domain object and populating the properties on the view model.
summation: build the view model to be only things that the view needs and write mapping logic to populate that view model.
2) This is just a statement of best practice before I breakdown your specific scenario.
You describe your architecture as having a BLL and DAL. If that is the case then you should not be persisting any objects from your controller. The controller should not have any knowledge of the database even existing and the objects used in the controller should have no idea of how to persist themselves. The objects that are going between your controller and the web service should strictly be Data Transfer Objects (DTO's). If you are unfamiliar with what constitutes a DTO then I highly suggest that you do some research and try to build them into your solution. It will help you conceptually see the difference between view model objects, domain objects and data transfer objects.
3) I would not try to store an order object in the session. I would re-analyze how you are breaking up the partial views within the view so that you can call actions with the ordersviewmodel being the parameter in a way that you need. It sounds like you are needlessly breaking up views into partial views.
4) You should not be concerned with state management for the view model object. Your view (which can be comprised of many partial views) is filled based on properties provided by the view model. The user can make changes using the UI you have developed. Since you express the desire to only save once they are finished making all changes to optimize calls to the web service, you just need to repopulate the fields of the view model upon clicking submit. Now you have a "state" for orderviewmodel that represents the users changes. You can send this object to the web service after converting back to a DTO (if you do what I said above) or by mapping it to the domain object.
1 final note. You are using automapper to map your domain to the view model. I am assuming that your view model is too complex and includes things that you don't need because you built your view model to emulate the domain object so that automapper could map by naming convention. Automapper has an api for doing complex (custom) mappings that fall outside of standard same name properties. Don't let automapper constrain you to building your view models a certain way.
Hope this helps
In the MVC model, where does the responsibility of loading the view model lie?
Should the Controller load the data?
Should the View Model itself load the data ala:
MyViewModel viewModel = MyViewModel.Create(someValue);
Should a Service Layer load it ala:
MyViewModel viewModel = MembershipService.Instance.Load(someValue);
See this example of the really clean technique:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx
Alternatively you can do it manually: see "ASP.NET MVC In Action" book or CodeCampServer sources for examples. Basically you inject IViewModelMapper { public ViewModel Map(data); } to the controller. The neat thing is that it makes your IoC automatically pass services and repositories to your ViewModel mapper. However this can really make controllers be bloated with mapper interfaces so something like Jimmy Bogard's technique, even without AutoMapper, but with action filters than do pick IViewModelMapper, would be better.
If you can't do this, then I'd suggest stick with ViewModel handling mapping as Mathias suggested.
UPDATE: here's an example of automapper-like configuration with bits of CodeCampServer way. Not sure if it will work as is (or useful at all), just a demonstration.
public abstract class ViewModelMapper<Source, ViewModel> where Source: class, ViewModel: IViewModel
{
public abstract ViewModel Map(Source source);
}
public class ProductDetailsViewModel
{
public ProductViewModel Product { get; set; }
punlic IList<Language> AvailableProductLanguages { get; set; }
}
public class ProductDetailsViewMapper: ViewModelMapper<Product, ProductDetailsViewModel>
{
private ILanguageRepository languages;
public ProductDetailsViewMapper(ILanguageRepository languages)
{
this.languages = languages;
}
public override ProductDetailsViewModel Map(Product source)
{
var vm = new ProductDetailsViewModel();
AutoMapper.Map<Product, ProductDetailsViewModel>(product, vm);
vm.AvailableProductLanguages = languages.GetAppropriateFor(product);
}
}
public class ViewModelMapperActionFilter: ActionFilter
{
Type mapperType;
public ViewModelMapperActionFilter()
{
}
public ViewModelMapperActionFilter(Type mapperType)
{
}
public void OnActionExecuted(ControllerContext context)
{
var model = context.Result.ViewData.Model;
var mapperType = this.MapperType ?? this.GetMapperTypeFromContext(context);
// this is where magic happens - IoC grabs all required dependencies
var mapper = ServiceLocator.GetInstance(mapperType);
var method = mapperType.GetMethod("Map");
Check.Assert(method.GetArguments()[0].ArgumentType == model.GetType());
context.Result.ViewData.Model = method.Invoke(mapper, new[]{model});
}
}
public class ProductsController: Controller
{
[ViewModelMapper(typeof(ProductDetailsViewMapper))]
// alternatively [ViewModelMapper()] will auto-pick mapper name by controller/action
public ActionResult Details(EntityViewModel<Product> product)
{
// EntityViewModel is a special type, see
// http://stackoverflow.com/questions/1453641/my-custom-asp-net-mvc-entity-binding-is-it-a-good-solution
return View(product.Instance);
}
}
//Global.asax.cs:
IoC.Register(AllTypes.DerivedFrom(typeof(ViewModelMapper<>)));
The controller is the glue that binds the model and view together. You want both your model classes and views to have as few dependencies as possible on the other layers of your app. Therefore, the controller should always load data for the view, regardless of where the data comes from, or what design patterns you use in your model for retrieving it.
You may have a bunch of layers of abstraction for your data loading operations within your model layer, but the controller should be calling some method, or methods, which at some point down the call chain, goes to whatever persistent datastore you are using and gets the data needed for your view.
The controller should be providing all of the objects that the view needs, as this is really one of its key responsibilities. Whether that means using the appropriate model object to grab the data from a database, or initializing a "view model" class to wrap all the objects and properties needed for the view to display, it doesn't matter.
Personally, I have always used Linq-to-SQL in conjunction with ASP.Net MVC, and have had great success using repository classes that grab the necessary objects from the data context. To allow my controllers to be unit tested, I use a dependency injection framework (StructureMap in my case) to have ASP.Net MVC provide my controller with a default instance of my repository interface.
the controller should never load data. that should be in either the model or in a data layer.
my preference is in a data layer so i can seperate it from the model which i think isa there only to store/represent data that is given to the controller and then the view.
i implement the repository pattern for data retrieval
I like to have the ViewModel load the data.
ViewModels are accessible from all Controllers. That way its possible to avoid code dupplication.
See this sample ASP.NET MVC - Job of Controllers
I layer things this way:
view->controller->service->persistence
Requests flow from front to back; responses go from back to front.
The persistence layer worries about the database; it makes model data available. It knows nothing about any of the other layers.
The service layer's methods map to use cases. It knows about the persistence layer, units of work and transactions. It validates its inputs, acquires database connections, makes them available to the persistence layer, cleans up resources, and works with model objects to fulfill the use cases. It can be exposed as a web service or EJB if needed.
The controller and view go together. It knows about the service layer. It maps requests to services, binds and validates incoming request parameters, passes them to the service layer, and routes responses to the appropriate view.
The view only worries about displaying the data that the controller provides for it. It knows about the controller.
So it's the service that deals with the database. Controller and view collaborate to display info, nothing more.
I'm currently in the process of converting some small personal web sites from WebForms to MVC. With the existing sites, the database schema is solid but I had never really taken the time to build proper data/business models/layers. The aspx pages all talked to the database directly using a variety of Views and Stored Procedures that were created as needed for convenience. With MVC, I'm now trying to "do it right" as they say and use things like LINQ to SQL and/or the Entity Framework to build a proper data model or models for the application.
My question revolves around what goals I should have for building data models. I've read various pattern related articles and I realize that ultimately the answer is likely going to depend on the characteristics of my data. But generally should I attempt to build bigger models that encompass as much of the database as possible so that there's only one way to interact with a given set of tables? Or should I build smaller custom models for each MVC View that only contain the data and access that View will need?
Or should I build smaller custom models for each MVC View that only contain the data and access that View will need?
This would probably be better.
Do not forget, you can stick your models in hierarchies, so common properties, like ids, names, preferences can be present in each model.
Fat expanded models could be better for enterprise application, where framework automatically does lot of stuff based on preloaded user preferences, user roles, access rights etc. For a small personal project would probably be better to try to keep your models small and clean. It is also a protection. By not putting unnecessary data into a model you ensure your view will not by mistake display wrong entries or submitting a form would not by mistake overwrite some other data.
I would go for the model representing the actual data logic within your current system and have your controllers return the piece of the model which the view needs such as:
Controller:
public ActionResult index()
{
var ListOfObjects = DataHelper.GetAll();
ViewData.Add(ListOfObjects);
return View();
}
public ActionResult ViewObject(int id)
{
var Object= DataHelper.GetObject();
ViewData.Add(Object);
return View();
}
public ActionResult ViewObjectChild(int Objectid, int ChildId)
{
var Child= DataHelper.GetChildObject(Objectid, ChildId);
ViewData.Add(Child);
return View();
}
On the view
/
<% var myListOfObjects = ViewData.Get<IList<Object>>(); %>
/ViewObject/1/
<% var myobject= ViewData.Get<Object>(); %>
/ViewChild/1/1/
<% var myChild = ViewData.Get<Child>(); %>
Note I have used MVC Contrib typed functions I highly recommend these.
Generally, you would have one comprehensive domain model for the database. You can use (modify/add/remove/etc.) the domain model in your service layer or the controller if it is a small app.
However, for your views, you can use presentation objects to make the views easier to maintain. These are sometimes also called DTO or view model objects. Basically what you do is create an object that contains all the data from the model that is necessary for the view to be populated.
For example:
Your model may include:
public class Car()
{
public string Model;
}
public class Driver()
{
public string Name;
}
You want the view to output the name and model of the car and you would have to pass both the Car and Driver model objects the view.
Rather than sending the two model objects directly from the controller to the view, you can create an object which contains just the data you need:
public class CarAndDriverViewModel()
{
public string CarMake;
public string DriverName;
}
You would populate this object from the domain data and pass that to the view. And the view would be:
model.DriverName + ": " + model.CarMake
Now you don't have to worry about lazy loading issues or complicated view logic to deal with model peculiarities. It's more work to create these view model objects but they really help keep the view clean and provides an easy way to do formatting before sending data to the view.
There are projects and conventions you can use to help automate the creation of the view models, if you want to look into them. AutoMapper is an example.