Cross-Platform Mobile Architectural Patterns

This post contains links and references to subjects discussed in my MonkeySpace 2013 talk.

Naturally, the talk was grounded in Xamarin Studio.

Xamarin’s Field Service App is a reference implementation for cross-platform MVVM.

The Xamarin DCI example source code can be found here.

Essential books on software-architectural patterns:

Special mention should be made of:

Which covers Data-Context-Interaction in detail.


References:

It should be noted that DCI advocates now speak of it as a “paradigm” and not simply an architectural style.

More DCI links:


The quote by Joe Armstrong:

I think the lack of reusability comes in object-oriented languages, not functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

If you have referentially transparent code, if you have pure functions — all the data comes in its input arguments and everything goes out and leave no state behind — it’s incredibly reusable.

comes from the book Coders at Work.


I write about software development in my monthly column for SD Times.

Using Extension Methods on a C# Interface to Enable DCI in Xamarin

Scala has several nice language features, including the elegant use of val for immutable variables and var for mutable, but the feature that I miss the most on a day-to-day basis is “traits.”

Traits allow you to implement one or more methods of an interface. The canonical use is to “mix-in” behavior while avoiding the “diamond-problem.”

DCI has the idea that Objects (domain-meaningful entities that correspond to user conceptions) adopt Roles, which are context-specific. Roles interact to produce value. So, for instance, when you’re transferring money at an ATM, you’re dealing with two accounts that are the same type of Object (Account), but which are in two different roles in the context of “Transfer Money”: a TransferSource and a TransferSink. And an Account in a TransferSource role has different behavior than an Account in a TransferSink role (e.g., TransferSource expects to withdraw(Money amount) while TransferSink expects to credit(Money amount)).

In C#, the way to specify that a class has a certain set of behaviors is to specify those behaviors in an interface and specify that the class implements them:

public class Account: TransferSource, TransferSink

And then, of course, you would implement the various methods of TransferSource and TransferSink within Account.

But the very essence of DCI is the premise that classic OOP type-systems don’t appropriately capture the relationships between Objects-in-Roles, even though “Objects-in-Roles working with each other” is the domain-users mental model (“I pick a source account, and a destination account, and specify an amount, and the amount is debited from the source and credited to the destination”). So DCI says that the TransferTo method that corresponds to the use-case should be elevated to a first-class object.

But in C# you cannot partially implement an interface. But you can create and implement an extension method on an interface!

  public static class TransferContextTrait
  {
    public static void TransferTo(this TransferSource self, TransferSink sink, Decimal amount)
    {
        try
        {
            if(self.Funds < amount)
            {
                self.FailTransfer(new TransferFailedReason("Insufficient Funds"));
            }
            else
            {
                self.Withdraw(amount);
                sink.Deposit(amount);

                var details = new TransferDetails(self.Name, sink.Name, amount);
                self.AccomplishTransfer(details);
            }
        }
        catch(Exception x)
        {
            self.FailTransfer(new TransferFailedReason(x.ToString()));
        }
    }
}

Note an interesting restriction, though: You cannot trigger an event from within an extension method! So in this case, although I would have preferred to propagate the results of the calculation by self.TransferAccomplished(this, details) I have to use a proxy function in Account:

public void AccomplishTransfer(TransferDetails details)
{
       TransferAccomplished(this, new TArgs&lt;TransferDetails>(details));
}

public event EventHandler&lt;TArgs &lt;TransferDetails>> TransferAccomplished = delegate {};

I’ll be talking more about DCI and other cross-platform architectural techniques at MonkeySpace in Chicago next week. Hope to see you there!