Question

My simplest ASP.NET MVC 2 controllers make calls to my service layer and map view models to entities using AutoMapper. Everything looks fantastic and there is no repeated code.

However, when I get into scenarios where I have similar behavior I have trouble balancing Single Responsibility Principle (SRP) with Don't Repeat Yourself (DRY). An example of this might be the need to add/edit vehicles where some properties/behaviors are shared while others are unique to a specific vehicle.

If I strive for really thin controllers (thus honoring Single Responsibility Principle), I end up having repeated code in both the views and controllers with minor variations (title, field labels, field visibility, dropdown values, selection criteria, etc.).

If I strive for non-repeated code I end up bundling too much logic into a single controller/view and it gets bloated.

What are some ways of addressing repeated code in controllers / views? I'm not talking about database code that can be factored out to a repository. Nor am I talking about business logic that can be factored out to a service layer. I'm looking for tools and/or rules of thumb that will help me produce the best solution in the scenario described above.

Was it helpful?

Solution

You get:

  • partials
  • RenderAction
  • action filters
  • service layer and helper classes (not HtmlHelper)
  • model binders
  • base controllers
  • dependency injection

So your views can invoke shared partials/actions for similar parts, common data can be prepared by action filters, database access code can be hidden in smart model binder, or you can have parent controller that child controllers override with specific tweaks. And, of course, good old service layeres, where you just extract common code into helper/static methods, or, better, inject specific implementations.

That's nothing new, same old tricks.

Or, maybe, your controllers do too much works? This is where stuff above also helps. ASP.NET MVC has very good tools to hide infrastructure-layer code and move it away from controllers. And if it's not infrastructure - it probably belongs to domain layer. There you can use inheritance, composition and other OOP tricks.

Specific example. Suppose your controllers should set few properties in a different way.

  1. You can have your views to do this, if it's mostly formatting or choosing what properties to show
  2. You can have your entities to have virtual methods - i.e. refactor code to move decisions to domain layer instead of controllers
  3. You can have helper ViewDetails classes that will take your entities and get the data based on for what you need it; this is a bit of a dirty trick but sometimes useful; you delegate decision to another "strategy" class
  4. You can use action filters to add this data to ViewData, or to tweak specific ViewData.Model types (look for some interface of it).
  5. You can have abstract controller, where children pass implementation details to the base constructor like (): base(repository => repository.GetSpecificData())

And so on. I actually use all of them in appropriate places.

OTHER TIPS

You are worrying too much about SRP and DRY. They are principles only and are not always right. SRP and DRY are good if they make your code more maintainable, but if they are in the way then ignore them. MVC is similar. It is useful in simple small desktop applications but is not appropriate for web applications. Web Forms is much better for the internet world while MVC is something from the 1980s.

I recommend you to use SRP over DRY in those cases. I wrote here a detailed answer.

In short both are rules which help to keep your code maintainable. DRY is a low abstraction level mechanism, while SRP is a high abstraction level. By maintain an application the high abstraction level structure is more important than the low abstraction level.

In your case I don't think it is necessary to give up DRY.

An example of this might be the need to add/edit vehicles where some properties/behaviors are shared while others are unique to a specific vehicle.

Many design patterns can help in this case. You can use decorator, composition, and so on... combined with builders for the different types of vehicles.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top