1 or 3, not 2. Provided that if you do # 3, you actually do not allow the static method to make web service calls, just let it do the mapping. The domain object is in, viewmodel (s). Prefer the extension method for an overloaded constructor, if the object does not need to track the state, there is no benefit in making it non-static.
Why?
As soon as you put the boolean method into the model, it ceases to be POCO. Best practice is to handle view models as much as possible, such as boring data buckets. Some people also try to make a mapping in the viewmodel constructor, which is not a good idea when you get into any kind of display complexity.
If you only need to do the mapping in one controller, you can put it in a subroutine. Keep in mind if you want to test sub in isolation and keep it internal, your project will need to have InternalsVisibleTo your test project.
Update
Looking at my code, I tend to agree with @C Sharper that this applies neither to the controller, nor to the viewmodel, nor to the helper class / method. Compiling this ChartsModel is very interesting code and contains a lot of business logic. It really should be in a separate layer. Your controller should go to this layer and delegate all this interesting and important code to another abstraction. This abstraction should then return the domain object, as @C Sharper said. Regardless of whether you use this domain object as your view model or DTO in another view model, it is up to you. Here's what it looks 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); } }
This is a good piece of the controller. Then the abstraction might look something like this:
public class ChartDataComposer : IComposeChartData { public ChartData ComposeChartData(ChartCompositionSettings settings) {
Thus, your view model should not move to a separate layer, but you need to create a similar object (ChartData) in this layer. The interface separates your controller from the data it needs, and the returned object binds the presentation data (viewmodel).
I think that I really do not see this code as business logic, but more like presentation logic. Why do you see it as business logic?
Think of your RateHistoryService class as a provider. You get raw materials from it and turn these raw materials into something else, creating value in this process. This is what companies do.
In this case, the visualization of the chart is the value you provide. Otherwise, your customers will have to sift through the raw data, crop, classify, sort, group, etc., before they can create similar charts.
I probably should have explained this earlier, but the call to the service is already at our own business level and the domain level of business objects is returned. It seems strange to me to have more than one business layer.
Your business level may have its own internal bundle. In this case, you can create a RateChartingService that uses the RateHistoryService to return the RateHistoryService business object. Then map this to ChartsModel (or, as I said earlier, use it directly as your view model).