Showing posts with label DCI. Show all posts
Showing posts with label DCI. Show all posts

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  }
   10 

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 }
   68 

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 }
   77 

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;
   83 
   84     ctx.Execute();
   85 
   86     return ResultPage(ctx);
   87 }
   88 

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.

Thursday, January 13, 2011

Doing DCI with ASP.NET MVC

The DCI - Data, Context, Interaction - paradigm (intro article, an earlier post) brings the roles domain objects play at runtime to center stage. DCI also fits right into the gaps of MVC. What? Which gaps?

MVC Gaps
Back in the day, when MVC was originally described by Trygve Reenskaug as an architecture approach it was all about the users mental model, giving the user the sense of just working directly with the domain objects. Now dont confuse this with the dreaded CRUDy forms-over-data battleship grey enterprise app. Those apps force the user to manipulate the data directly; respecting, fighting and being forced into submission by the techniocal details of databases. MVC on the other hands puts that the model is the users mental model - the programmer, then has to deal with hiding the technicalities of persistence and such. As illustrated below the users works as if manipulating the domain objects directly. The view simply reflects the model state, the controller reacts to user actions, and translates them to model updates, and that's it.
Direct manipulation metphor as illustrated by Trygve Reenskaug
This is fantastic for fairly simple apps. But as soon we introduce business rules with a certain amount of complexity, the direct manipulation metaphor does not suffice anymore. We need somewhere to put those business rules.  -The controller already has clear responsibility, so in keeping with single responsibility principle that's not the place for the behaviour. -The traditional OO answer is to put the behaviour into the model, but that leads to hard-to-follow logic jumping back and forth in the model objects, and (gark) up and down long inheritance chains. Moreover it turns out that a given domain object will play different roles over times. I.e. the object doesn't need just one behaviour, it needs several, and even worse it needs different behaviours over time. There goes SRP again, only this time in the "intelligent model".

Roles and  Contexts to Fill the Gap
So we need something more than MVC: The view is not the place for the behaviour, the model is not the place for the bahivour, and the controller is not the place for it either. This is where roles as separate things come into play. We want explicit roles. We want those business rules implemented in the roles. And we want to assing those roles to domain objects as needed.

The role a given object plays at a given moment depends on the context at that moment. So why not introduce contexts as something explict along with the explicit roles? Together roles implementing behaviour and contexts assigning roles to objects fill the gap in MVC.

Back to APS.NET MVC
Lets get practical: Where do the contexts and the roles go then? Since the contexts manage assigning roles and kicking off the behaviuour the contexts and roles fit well together. In the ASP.NET MVC + DCI demo I did for ANUG I organized the code like this:

 The Contexts folder has a subfolder for the use case I demoed. Other use cases would have their own subfolders. Inside the subfolder is the context for executing that use case, and the associated roles.

What is a role in C#? A role in C# is a set of extension methods on an interface (see this and that for more on roles in C#):

    1     public static class TransferMoneySourceTrait
    2     {
    3         public static void TransferTo(this TransferMoneySource self, TransferMoneySink recipient, decimal amount)
    4         {
    5             // The implementation of the use case
    6             if (self.Balance < amount)
    7             {
    8                 throw new ApplicationException(
    9                     "insufficient funds");
   10             }
   11 
   12             self.Withdraw(amount);
   13             self.Log("Withdrawing " + amount);
   14             recipient.Deposit(amount);
   15             recipient.Log("Depositing " + amount);
   16         }
   17     }

How does the context track the role assignments? Simple:

    8     public class TransferMoneyContext
    9     {
   10         public TransferMoneySource Source { get; private set; }
   11         public TransferMoneySink Sink { get; private set; }
   12         public decimal Amount { get; private set; }
   13 
   14         public TransferMoneyContext(TransferMoneySource source, TransferMoneySink sink, decimal amount)
   15         {
   16             Source = source;
   17             Sink = sink;
   18             Amount = amount;
   19         }
   20 
   21         public void Execute()
   22         {
   23             Source.TransferTo(Sink, Amount);
   24         }
   25     }
   26 

Where are the objects mapped to roles? Right in the seam between the controller and the context (lines 35 to 37):

   11     public class TransferMoneyController : Controller
   12     {
   13         AccountRepository accountRepo = new AccountRepository();
   14 
   15         public ActionResult Index()
   16         {
   17 
   18             ViewData["SourceAccounts"] =
   19                 accountRepo.Accounts.Select(a => new SelectListItem {Text = a.Name, Value = a.Id.ToString()});
   20             ViewData["DestinationAccounts"] =
   21                 accountRepo.Accounts.Select(a => new SelectListItem {Text = a.Name, Value = a.Id.ToString()});
   22             return View();
   23         }
   24 
   25         [AcceptVerbs(HttpVerbs.Post)]
   26         public ActionResult Index(FormCollection form)
   27         {
   28             var sourceAccountId = form["SourceAccounts"];
   29             var destinationAccountId = form["DestinationAccounts"];
   30             var amount = form["Amount"];
   31 
   32             var sourceAccount = accountRepo.GetById(int.Parse(sourceAccountId));
   33             var destinationAccount = accountRepo.GetById(int.Parse(destinationAccountId));
   34 
   35             new TransferMoneyContext(sourceAccount as TransferMoneySource,
   36                 destinationAccount as TransferMoneySink,
   37                 decimal.Parse(amount)).Execute();
   38 
   39             ViewData["Amount"] = amount;
   40             ViewData["Source"] = sourceAccount.Name;
   41             ViewData["Destination"] = destinationAccount.Name;
   42 
   43             return View("Result");
   44         }
   45     }

Apart from lines 35 to 37 this is all just standard ASP.NET MVC stuff: Getting data in and out of the ViewData, and returning the view to render back to the user.

Summing Up
MVC is great for simple situations. It doesn't quite scale to complex business scenarios, but DCI does, and the two are a great match.
Leveraging the power of C# to do DCI and the niceness of ASP.NET MVC to go along with it, makes for a - IMHO -very compelling architecte style for an enterprisy web app.

Monday, December 20, 2010

Options for DCI on .NET

Here's a short summary of the technical/language options for doing DCI on the .NET platform. The list below is loosely ordered in order of increasing "mainstreamness" - according to me :-).
The list notes the language, how roles and contexts are handled and how the solution is run on .NET.

  • Scala "cross-compiled" to MSIL/CLR:
    In Scala contexts are simply objects instantiated at runtime from context classes. The contexts have the responsibility of instantiating data objects and mapping roles to them. Roles are traits that are mixed into data objects at instantiation time. So typically the contexts receive ids for data objects, pull the corresponding data from e.g. a database, and instantiate the data object with a role trait mixed in. In Scala this is all strongly typed and looks like this:

    class MoneyTransferContext(sourceAccountId: int,
                                sinkAccountId: int){
       val source =
            new SavingsAccount(sourceAccountId) with TransferMoneySource
       val sink =
             new CheckingAccount(sinkAccountId) with TransferMoneySink
       def execute = {
         source.deposit(100000)
         source.transferTo(200, sink)
         }
    }

    To run this in a .NET setting the Scala code must "cross-compiled" to MSIL, and must use the .NET standard library along with the Scala APIs. For details on this see my earlier post, or the introduction on the Scala language web site.

  • Python running on IronPython:
    In Python contexts are objects, that take the responsibility of mixing roles into data objects dynamically. The data objects can be instantiated outside the context or inside the context depending on taste. When the context is done the roles can be yanked back out of the data objects. I dont know a whole lot of Python, so I wont go into details, but refer you to Serge Beaumonts sample.
    To run this on .NET use IronPython.
  • Ruby running on IronRuby:
    In Ruby contexts are also objects, that take the responsibility of mixing roles into data objects dynamically. Data objects can be instantiated inside the context or outside the context. Roles in Ruby are implemented as modules, which using the Ruby standard library can be mixed into any Ruby object. The Ruby code looks like this:
    class TransferMoneyContext
       attr_reader :source_account, :destination_account, :amount
       include ContextAccessor
       
       def self.execute(amt, source_account_id, destination_account_id)
         TransferMoneyContext.new(amt, source_account_id,
                                  destination_account_id).execute
       end
     
       def initialize(amt, source_account_id, destination_account_id)
         @source_account = Account.find(source_account_id)
         @source_account.extend MoneySource
         @destination_account = Account.find(destination_account_id)
         @amount = amt
       end
    
       def execute
         in_context do
           source_account.transfer_out
         end
       end
    end

    To run this on .NET just use IronRuby. It runs fine out of the box.

  • C# and Dynamic:
    Using the dynamic features of C#, contexts are objects that receive either data objects or IDs of data objects. In case of an ID the context instantiates the data object and populates it based on the ID and some data source. In both cases the data object has a role reference which will point to an expando object, into which the roles methods are injected. The code looks roughly like this:
    dynamic transfer = new ExpandoObject();
     transfer.Transfer = (Action)
      ((source, sink, amount) =>{ 
        dynamic mSource = source;
        dynamic mSink = sink;
        mSource.DecreaseBalance(amount);
        mSink.IncreaseBalance(amount);});
     Account sourceAcct = new Account { CashBalance = 100m };
     sourceAcct.Role = transfer;
     Account sinkAcct = new Account { CashBalance = 50m };
     sourceAcct.Role.Transfer(sourceAcct,sinkAcct,10m);
    Running on .NET? Well, it's C#, so just do it.

  • C# and extention methods:
    Using C# extension methods for roles contexts are objects that receive either data objects or IDs of data objects. In either case the context maps the data objects to roles. Roles are implemented as static classes containing extension methods for role interfaces. Data objects capable of playing a given role implement that role interface. The code looks like this:
    public class TransferMoneyContext { public TransferMoneySource Source { get; private set; } public TransferMoneySink Sink { get; private set; } public decimal Amount { get; private set; } public TransferMoneyContext(TransferMoneySource source, TransferMoneySink sink, decimal amount) { Source = source; Sink = sink; Amount = amount; } public void Execute() { Source.TransferTo(Sink, Amount); } } public interface TransferMoneySink { void Deposit(decimal amount); void Log(string message); } public interface TransferMoneySource { decimal Balance { get; } void Withdraw(decimal amount); void Log(string message); } public static class TransferMoneySourceTrait { public static void TransferTo(this TransferMoneySource self, TransferMoneySink recipient, decimal amount) { // The implementation of the use case if (self.Balance < amount { throw new ApplicationException("insufficient funds"); } self.Withdraw(amount); self.Log("Withdrawing " + amount); recipient.Deposit(amount); recipient.Log("Depositing " + amount); } }

    How to run this on .NET is ... well ... pretty obvious :-)

Friday, November 26, 2010

Slides from DCI talk at ANUG

I did a talk on DCI at my local .NET User Group, ANUG, the other night and had a great time. The slides from the talk were:




The slides were followed by three demos:

  1. A simple Scala example "cross"-compiled to run on .NET
  2. A simple Ruby example running on IronRuby
  3. A slightly larger ASP.NET MVC based C# example, which I will be posting details about in the furture.

Monday, May 4, 2009

DCI in C#

The DCI, or the Data Context and Interaction is an approach to implementing domain logic and use cases in terms of the roles domain objects play at runtime. It is being developed and promoted by Trygve Reenskaug and James O. Coplien, who have written a very good introductory article about DCI at Artima.com. Here I'll stick to a very brief explanation and then jump into implementation of DCI in C#.

DCI at a Glance

I find that the DCI design is most easily understood in the context of Model View Controller. MVC, in its purest form, allows the end user to reach through the view, via the controller into the model and manipulate it directly. This is fine as longs the user is actually able to perform his tasks simply by massaging the domain objects. As soon as the application needs to support some sort of coordinated sequence of actions that the user is not allowed to control on his own,  something more is needed. Where is that control coded? Is it in the controller? Is it spread out between a number of domain classes? Is it in a business or service layer on top on the domain model? -DCI offers a different take:
DCI tells us to think in terms of three main concepts: The data captured in the domain model, the interactions between domain objects happening at runtime and the context in which the interactions happen. DCI also tells us to implement interaction in terms of roles: Domain objects take on different roles throughout the execution of an application. The interactions between domain objects happen in terms of these roles. The context of an interaction is the collection of runtime objects that will take part in the interaction, each referenced by role. The nice thing is that those coordinated sequences of actions are implemented directly in the role that will plays the lead part in the interaction. The context will provide the role with all the collaborators it needs. 
Right, enough explanation, the article mentioned above does a better job at explaning DCI anyway, so I'll just jump right into the C# code.

The C# Code

I'll walk through how the data, the context, and the role can be implemented in C#. To do this I'll use a simplistic banking example and implement a simplified money transfer use case in the role TranferMoneySource. The example is the same as the one used in the article linked above.
The domain model (i..e the D in DCI) consists of a savings account and an abstract account:

public abstract class Account
{
   public double Balance { get; protected set; }

   public void Withdraw(double amount)
   {
       Balance -= amount;
   }

   public void Deposit(double amount)
   {
       Balance += amount;
   }

   public void Log(string message)
   {
       Console.WriteLine(message);
   }
}

public class SavingsAccount 
    : Account, TransferMoneySource, TransferMoneySink
{
    public SavingsAccount()
    {
        Balance = 1000;
    }

    public override string ToString()
    {
        return "Balance " + Balance;
    }
}

The TransferMoneySource and TransferMoneySink are roles, and I'll get back to those. For now just notice that the roles must be interfaces, since the single inheritance of C# is used to extend the abstract Account.
The context (the C) in which the money tranfer use case is executed is the TransferMoneyContext:

public class TransferMoneyContext
{
    public TransferMoneySource Source { get; private set; }
    public TransferMoneySink Sink { get; private set; }
    public double Amount { get; private set; }

    public TransferMoneyContext(TransferMoneySource source,
        TransferMoneySink sink, double amount)
    {
        Source = source;
        Sink = sink;
        Amount = amount;
    }

    public void Execute()
    {
        Source.TransferTo(Sink, Amount);
    }
}

The context captures the objects necesarry to execute the money transfer use case in terms of the roles they play in that use case. I.e. the context captures the account that acts as source and the account that acts as sink. The context just holds on to these object until the Execute method is called. At that point the control is transferred to the role capable of running the use case - the source account.

Lastly the role implementation remains. The use case (the I) is captured in the role, and the role is attached to any number of domain objects.  As mentioned above the roles are interfaces, but they also need to implement the use case. This is achieved through extension methods attached to the role interfaces. This enables us to tie behaviour to interfaces, which is what we need in order to tie the use case implementation to the role, which in turn can be tied to any domain object. The roles are implemented like this:

 public interface TransferMoneySink
{
    void Deposit(double amount);
    void Log(string message);
}

public interface TransferMoneySource
{
    double Balance { get; }
    void Withdraw(double amount);
    void Log(string message);
}

public static class TransferMoneySourceTrait
{
    public static void TransferTo(
        this TransferMoneySource self,
        TransferMoneySink recipient, double amount)
    {
        // The implementation of the use case
        if (self.Balance <>
        {
            throw new ApplicationException(
                "insufficient funds");
        }

        self.Withdraw(amount);
        self.Log("Withdrawing " + amount);
        recipient.Deposit(amount);
        recipient.Log("Depositing " + amount);
  }
}

In essence the above usage of extension methods on interfaces emulates traits in C#.

DCI on .NET

While this offers one way of doing DCI on the .NET platform it is not the only way. There are also implementation in Python and Ruby. Attaching roles to objects is somewhat more straight forward in Ruby, where it is done by mixing a module specifying the a role into a domain object at runtime. With IronRuby maturing nicely, that could easily turn out to be the nicer DCI implementation for .NET.