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.

38 comments:

  1. Thanks Christian.. I'm looking forward to more complex examples. I'd really like to see a full stack implementation [ UI and all ]. Would be fantastic to get a compare / contrast (DCI versus locating behavior on domain object). If you see any, do tell. -Alan

    ReplyDelete
  2. I like the idea of a context object with objects named and performing in their roles for complex transactions. This also nicely complement's Udi's 'making roles explicit' role interfaces.

    Not so sure about using extension methods to encapsulate use cases however - that seems like anemic domain. Instead I would argue that in your example the whole TransferTo() should be a method on a Customer role class.

    ReplyDelete
  3. @Richard
    The domain model in DCI is stupid. It only does really simple tasks on its own. Everything else is implemented as methods on roles. At the same time roles may be played by more than one type of domain object and each domain object may play more than one role. In C# that leaves us with a problem: We dont want to replicate the role method code in several domain classes, but we cannot place it in a super class either because of single inheritance. That's why I ended up using extension methods on interfaces: They can be mixed into all the domain classes I want, and each domain class can have as many as I want.

    On the other hand I agree that this all feels sort of like a hack compared to achieving the same thing via traits in Scala or mixins in Ruby.

    ReplyDelete
  4. Please refer to colormodeling approach .. DNC

    ReplyDelete
  5. Can I said that TransferMoneyContext act as a service/facade?

    ReplyDelete
  6. @ryzam
    I would not call the TransferMoneyContext neither a service nor a facade. The job of the context is different from that. The context manages the mapping between the roles needed in a given use case and the runtime objects playing those roles. I.e. the context object provides the context in which the use case can be executed.

    ReplyDelete
  7. But I smell it's look like a DomainService, you can refer to Domain Driven Design camp.

    Based on my experiance in ColorModeling, I will name it as MoneyTransfer (MomentInterval). I'll try to send to you what it's look like in color modeling domain design

    ReplyDelete
  8. @ryzam
    The interface to the Context does look like a Domain Service, but I think the role is slightly different. I might make sense to wrap a Context, and the execution of a use in a Domain Service in certain cases. A possibility along those lines is expose the app through a REST API in which the resource URIs translate directly to contexts (this is something Rikard Oberg is exploring)

    ReplyDelete
  9. Interesting of seperating the "use case" / "algoritm" from the domain objects.
    Having the code for a role in a separate part could also be done in a partial class, but this is a lot more flexible.
    For me this is the 3de way to do this. The 1st way would be a pattern like Mediator that holds the parts together like a chassis of a car or Facade pattern to make the objects work as a service.
    2de way is the concept of "pure" functions. Where you pass all of the context as arguments and never use a member variable directly. Possibly reducing the number of arguments by binding them together in an Args object, similar to EventArgs.

    I like the 1ste for it's "real world" feeling and because the starting point of the algorithm should be itself not some object it uses.
    I like the 2de because it reduces clutter. Everything the algorithm uses is passed, so there are no side effects.
    This 3de way is for me a little too much code for what it's trying to do. Defining interfaces for the arguments is fine industrial strength technique, for an extra bit of guarding and decoupling, but the extension method makes the algorithm be invoked on an object where I would not look for it in the first place. Maybe it would just take a little getting used to.

    ReplyDelete
  10. I've try your code, but i don't see any advantage of adding traits in C#

    ReplyDelete
  11. @ryzam: well that's sort of how linq works...isn't it?

    ReplyDelete
  12. Hi Horsdal

    Please take a look at my example

    http://www.pastie.org/1016844

    try to add color modeling with DCI

    ReplyDelete
  13. @ryzam: What is the important difference between your example and the code in this post?

    ReplyDelete
  14. Not much different, most of my example code is refer to your code, i just add some amendment this is related to persistence issue (NHibernate).

    MoneyTransfer is Moment Interval for me based on my understanding from color modeling camp, but you refer it to context which is DCI approach.I also make MoneyTransfer as an abstract so that i can instantiate it with concrete object like SavingToCurrentAccountTransfer (easy to persist).

    I've a working example using WinForm + NHibernate + Autofac. Please email to me if interested

    ReplyDelete
  15. I attended the whole DCI track held by Trygve and James at last year's Øredev in Malmö.

    I asked James if Coad's DNC could be applied in order to solve the same problem in C# since we don't have mixins, nor a dynamic language (we have dynamic, ExpandoObject(), and DynamicObject as of now).

    He said he only knew vaguely about Coad's work in the area, and therefore couldn't answer the question.

    For me, having implemented DDD and DNC in C# in the past, DCI is basically a more elegant solution to the same problem as DNC tries to solve if you're in a language environment that fully supports it. For that reason; I have continued to use DNC in C#.

    ReplyDelete
  16. @Lars-Erik:
    I mostly agree with your comment. I also tend to prefer very specific names, so if there are separate SavingsAccount roles I would agree that their usage should be specific in the names.

    In real world scenarios I would have the names of contexts correspond to use case names.

    I generally dont like to include things like 'core' or 'base' or ... in identifiers; it feels unnatural to me.

    And: Thanks for your feedback.

    ReplyDelete
  17. The issue I possibly see is that instance methods have a priority over extension methods in C# whereas in a "DCI vision" role methods (implemented in this post's example as extension methods) has a priority over RolePlayer's methods.

    ReplyDelete
  18. @Vitalii That's a good point. OTOH it's also about making do with what is possible in current tools.

    ReplyDelete
  19. Great article!

    I borrowed some of your code for a project I created here: http://joel.net/data-context-and-interaction-dci-windows-workflow-csharp

    ReplyDelete
  20. Seems similiar to Composite Oriented Development, and something Udi Dahan talked about.

    The Dynamic Proxy library (part of Castle) and it's ability to create mixins should allow for full DCI for .net.

    ReplyDelete
  21. @A'braham: By Composite Oriented Development do you mean stuff like Qi4J? -In which case yes there certainly an overlap with DCI. I believe one largest DCI systems out in the wild today is build using Qi4J.

    You can get a fair bit of the way with dynamic proxies (like Castles og Linfus) but you typically dont get all the way, since you have some sort of a wrapper around your objects at run time, which may lead to problems with object schizophrenia.

    ReplyDelete
  22. Thanks for the clarification. I just thought something was missing to the C# code example in the 'Lean architecture' book :-)

    ReplyDelete
  23. A very interesting sample. I would be very interested in studying a complete example.

    Best regards,
    Marcus Nordquist

    ReplyDelete
  24. @Marcus:

    You may want to go to http://fulloo.info/ where there is a download page, where you can Trygve's canonical BabyIDE sample.

    You may also find the collection of samples at https://github.com/DCI/dci-examples interesting.

    ReplyDelete
  25. @Christian: object schizophrenia? Please explain. It's a shame that implementing this approach is more suited to a dynamic language. As you said you can do in C#, but you'll end up making C# act like a dynamic language. Linfu's implementation is example of that. I want a Ruby definition of a mix in, mixins being able to interact with the calling class or other mixins. Spent a lot of time past few days researching this to come to this conclusion. Oh well, guess I'll keep learning Node. :)

    ReplyDelete
  26. I admit I am new to DCI, but I think your implementation does the right thing in the wrong place. As far as I am concerned the TransferMoneySourceTrait in not necessary at all - it's code belongs directly into the TransferMoneyContext.Execute (as it wont be used anywhere else).
    Traits are used to make the objects able to play their roles. In your case that is not even necessaray, because Account already has all functions.
    In the example of Coplien the account only had like: balance, add() and remove(). The MoneySource-Role then added Withdraw() - and THAT is the place where the source-account object needed to be be augmented: The sourceAccount needed to be extended to implement the withdraw...

    ReplyDelete
  27. @Fritz: The balance between what to put into the context and what to put into roles is one to some extent one of personal taste IMO. I think what you suggest makes sense.
    In this post I chose to keep the context small and focused only on setting up the context (pun intended), and then let the roles implement the business logic.
    But since roles should be scoped to a single context, this a local design decision that depends on how the code for a particular use case turns out best.

    ReplyDelete
  28. Christian, any idea on Unit Testing impact with DCI implemented as extension static method? Sure you can test them individually. But how to intercept them in mocks and stubs if you are testing the place where Role is used?

    ReplyDelete
  29. @Igor: I'd either not mock out the roles, but mock out the dumb data objects, or I'd mock out the whole context. Remember roles are scoped to a particular context in DCI, so their implementation should be seen as completely hidden to code outside the context.

    ReplyDelete
  30. In the original DCI documents we are not suppose to have a data field in the roles section (such as "Balance" property).
    And the another point is that all of the accounts have the "Balance" property, means that it is not related to a certain role.
    What is the resaon behind having the Balance in the "TransferMoneySoruce" ?

    ReplyDelete
    Replies
    1. Just because "Balance" is a C# property does not mean it's equal to a data field. It's just a getter, that could calculate the balance on demand or whatever you might like.
      The Balance is on the TransferMoneySource to enable the TransferTo method to check for sufficient funds before transferring, without resorting to downcasts.

      Delete
    2. This comment has been removed by the author.

      Delete
  31. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  32. Thanks Christian for the answer, I'm doing my master thesis based on the DCI, Now I'm implementing an small application based on the DCI architecture, I have several places that some Property (like the Balance) is defined in the role (like TransferMoneySource) just because the role method(like transferTo)need that data,
    I wanna see is there any possiblity (more nice looking) solution for using that that neither by down-casting nor by defining in the role ?

    *PS : having a property for getting access to data or even having the function returning a certain data is still something regarding to object not the role we just doing that because we don't wanna downcast the object!
    Is it(in original DCI document) such that we just can operational methods that actually related to the roles is the interaction part or we can have any kind of function (like a getter for example)?

    ReplyDelete
    Replies
    1. Regarding the PS: I'm not sure I understand your question - could you maybe give an example? (and tbh i think you'd get better answer if you post to the object-composition google group).

      Delete
  33. Any kind of answers can make me happy !

    ReplyDelete
  34. This is my implementation of the DCI in C# using the decorator pattern. What do you guys think.

    http://pastie.org/6403027

    ReplyDelete