0

I'm working on ASP.NET MVC project. In order to map view model to domain entity, I've decided to use a builder pattern. PtoRequest, UtoRequest are a part of the domain and inherit from abstract TimeOffRequest class which implements ITimeOffRequest. For each of them I have a separate builder. Each builder implements IRequestBuilder. The problem I'm having is how to enject those builders into the controller (Assuming I do not have to enject one builder for each type of request)?

  • Pto - paid time off
  • Uto - unpaid time off

Code:

public class TimeOffManager : Controller
{
   private readonly IUnitOfWork _uow;

   public TimeOffManager(IUnitOfWork uow)
   {
       _uow = uow;
   }

   [HttpPost]
   public ActionResult RequestPto(PtoFormVm vm)
   {
       //validate view model...

       ITimeOffRequest pto = new PtoRequestBuilder()
                               .Id(vm.Id)
                               .InRange(vm.StartDate, vm.EndDate)
                               .State((RequestState)vm.State)
                               .Note(vm.Comment)
                               .Build();
       // Etc...
   }

   [HttpPost]
   public ActionResult RequestUto(UtoFormVm vm)
   {
       //validate view model...

       ITimeOffRequest uto = new UtoRequestBuilder()
                               .Id(vm.Id)
                               .IsFullDay(vm.FullDay)
                               .InRange(vm.StartDate, vm.EndDate)
                               .State((RequestState)vm.State)
                               .Note(vm.Comment)
                               .Build();
       // Etc...
   }
}
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
Nik
  • 77
  • 6
  • Have you tried IOC Inversion of Control using any container? like, maybe Unity container? – venu Aug 07 '18 at 15:35
  • I am using a DI container to enject other dependencies via controller, but as I showed in the example, there are different instances of Builders in different action methods. I'm trying to figure out a way to pass them into controller as a dependency. – Nik Aug 07 '18 at 15:40

1 Answers1

1

Like this?

public class TimeOffManager : Controller
{
   private readonly IUnitOfWork _uow;
   private readonly IRequestBuilder ptoBuilder;
   private readonly IRequestBuilder utoBuilder;

   public TimeOffManager(IUnitOfWork uow, IRequestBuilder ptoBuilder, IRequestBuilder utoBuilder)
   {
       _uow = uow;
       this.ptoBuilder = ptoBuilder;
       this.utoBuilder = utoBuilder;
   }

   [HttpPost]
   public ActionResult RequestPto(PtoFormVm vm)
   {
       //validate view model...

       ITimeOffRequest pto = ptoBuilder
                               .Id(vm.Id)
                               .InRange(vm.StartDate, vm.EndDate)
                               .State((RequestState)vm.State)
                               .Note(vm.Comment)
                               .Build();
       // Etc...
   }

   [HttpPost]
   public ActionResult RequestUto(UtoFormVm vm)
   {
       //validate view model...

       ITimeOffRequest uto = utoBuilder()
                               .Id(vm.Id)
                               .IsFullDay(vm.FullDay)
                               .InRange(vm.StartDate, vm.EndDate)
                               .State((RequestState)vm.State)
                               .Note(vm.Comment)
                               .Build();
       // Etc...
   }
}

Apart from that, why do you have an abstract class that implements an interface? An abstract class is already polymorphic...

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Not sure if I could mock the abstract class though, that's why I used interface. Regarding passing builders into constructor... Is there a solid way to avoid passing them one by one? Besides `IUnitOfWork` I actually have three other parameters. If I add each type of builder, there would be about ten parameters in the constructor. Is that okay? – Nik Aug 07 '18 at 15:48
  • @НикитаУрюпин You can mock all virtual members of (abstract) classes: https://stackoverflow.com/a/1973482/126014 – Mark Seemann Aug 07 '18 at 15:52
  • @НикитаУрюпин *"If I add each type of builder, there would be about ten parameters in the constructor. Is that okay?"* No, it probably isn't. See: https://stackoverflow.com/q/2420193/126014 – Mark Seemann Aug 07 '18 at 15:54
  • `TimeOffManager` hardly violates SRP. Having a separate manager controller for each type of request is a lattile bit of a stretch. I guess I would have to find a perfect balance between # of parameters and class extraction... – Nik Aug 07 '18 at 16:07
  • Thanks for your suggestion though. It deffinetly helped! – Nik Aug 07 '18 at 16:08
  • @НикитаУрюпин One way to look at this is that the single responsibility of a Controller is to act as an [Adapter](https://en.wikipedia.org/wiki/Adapter_pattern) between HTTP and your domain or application model. Thus, ideally, you need exactly one dependency per action method. The single responsibility of each action method is to pull data from an HTTP request, delegate all work to the dependency in question, and translate the outcome of that call back to something that can go on the wire. So you shouldn't inject something like `IUnitOfWork` into a Controller, but one high-level dep per method – Mark Seemann Aug 07 '18 at 16:29