Wednesday, May 4, 2011

Building DCI Context with ASP.NET MVC

In this post I'll address one of the questions that I tend to get when talking about DCI: How is the DCI context built at runtime?

To that end lets expand a bit on the DCI with ASP.NET MVC sample that I showed in an earlier post. The focus of that post was how to fit ASP.NET MVC and DCI together in a nice cohesive architecture. In this post I will zoom in on how the controller can build up the DCI context object in a conversation with the user.

The example I'm using is still that of a very simplified online bank, where the user can trasfer money between accounts. The core DCI parts of this example is explained in this post, and the connection with ASP.NET MVC in this post. Whereas the sample in the last post simply asked the user to input all the information about the transfer in one screen, this version asks for the information one thing at a time in a wizard like fashion. Whether this is good UX, is besides the point of this post. I just want to use an example where the context is build in a series of interactions with the user.

Interaction Between User and Code
First off the user is asked to choose a source account on this simple (ugly) page:

Serving up this page is done by this rather straight forward action method:

    1  public ActionResult SelectSource()
    2  {
    3      Session["Context"] = new TransferMoneyContext();
    4      return View(new SelectAccountVm
    5                  {
    6                      SelectedAccountId = string.Empty,
    7                      Accounts = accountRepo.Accounts
    8                  });
    9  }

Worth noticing though, is that a MoneyTransferContext object is created and stored in the session.
When the user hits the Next button this action is hit:

   55 [HttpPost]
   56 public ActionResult SelectSource(SelectAccountVm model)
   57 {
   58     var ctx = Session["Context"] as TransferMoneyContext;
   59     ctx.Source = 
   60       accountRepo.GetById(int.Parse(model.SelectedAccountId)) as TransferMoneySource;
   61     return View("SelectDestination", 
   62                 new SelectAccountVm
   63                 {
   64                     SelectedAccountId = string.Empty,
   65                     Accounts = accountRepo.Accounts
   66                 });
   67 }

This code updates the context with the chosen source account, and serves the next view in the interaction, where the user is asked for a destination account:

When the user hits this Next button this action is hit:

   69 [HttpPost]
   70 public ActionResult SelectDestination(SelectAccountVm model)
   71 {
   72     var ctx = Session["Context"] as TransferMoneyContext;
   73     ctx.Sink = 
   74       accountRepo.GetById(int.Parse(model.SelectedAccountId)) as TransferMoneySink;
   75     return View("SelectAmount", new SelectAmountVm());
   76 }

which does almost the same as the previous action, except it puts the chosen account in the Sink property on the context, and then shows this page:

The post from this page does a bit more work. First it updates the context with the amount given by the user. At this point the MoneyTransferContext is complete and ready to be exectued. Finally a result page is shown:

   78 [HttpPost]
   79 public ActionResult SelectAmount(SelectAmountVm model)
   80 {
   81     var ctx = Session["Context"] as TransferMoneyContext;
   82     ctx.Amount = model.SelectedAmount;
   84     ctx.Execute();
   86     return ResultPage(ctx);
   87 }

and the result page is:

Summing Up

To sum up: To build up a DCI context in ASP.NET MVC simply store a context object in the session context when a use case is started and execute it when it makes sense from a functionality perspective. This way the controller code is kept very simple, concentrating only on building up the context through interaction with the user, and the details of the use case execution is - from the perspective of the cotroller - hidden behind the context. Furthermore because of the way the controller builds the context the context object only knowns the domain objects through the roles they have been assigned by the controller - or in fact the user - allowing for the full strenght of DCI to play out there.

You can find the complete code from this post on GitHub.