Feature Post

Top

How to think a unified design for multi device architecture?

Couple of weeks ago, I responded to question of similar sort, and then I thought I must elaborate on this using actual code snips, that may help anyone looking for similar solution.
 



Problem statement:

Build an internet based application that should be able to:

  • Process the incoming requests
  • Get records available in the data store
  • Perform actions on that data
Most importantly, a non functional requirement is that, the request may come from multiple devices, for instance PC, Handheld device, iPhone, Windows Phone, Android, etc. This means, solution should have one core functional module (Unified Functionality); that would serve multiple device ends.

Simple? It is.

There could be several ways of achieving that; lets discuss couple ways, and then elaborate the implementation.


SOLUTION 1: Use WCF based RESTful service with App_Browser feature



Why? Because it is easy, tidy, and quick.

What is required to achieve that?

Mark the web service methods with WebGet attribute, and set the ResponseFormat parameter to WebMessageFormat.Json.

For instance:
[OperationContract]
[WebGet(UriTemplate = "/DoSomething?intSomeType={intSomeType}", ResponseFormat = WebMessageFormat.Json)]
Response DoSomething (Int32 intSomeType); 


When the service is accessed RESTfully, it will return the data using the DataContractJsonSerializer. So you should JSONize(JSON Serialize) your POCO’s by marking it with the [DataContract] attribute and mark each serializable member as [DataMember].
Note that, this assumes that requests will only come from browser based user agent.

Also, the type hinting in WCF can add a lot of unnecessary overhead in the JSON response for large collections. Therefore, if you believe that you might want multiple types of end-points (SOAP, etc.) at some point in the time, then WCF is the way to go.


Easy? See! Let’s go through a demo app code right out of my pilot project.


Step 1: Write an interface that defines the basic operations that a mobile service will provide; and then provide an implementation of the interface:

[ServiceContract(Namespace = "izlooite.blogspot.services")]
public interface IMobileService
{
[OperationContract]
[WebGet(UriTemplate = "/ListBusinesses?intBusinessType={intBusinessType}", ResponseFormat = WebMessageFormat.Json)]
Response ListBusinesses(Int32 intBusinessType);
}


Step 2: Create WCF web service that implements IMobileService interface



[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MobileService : IMobileService
    {
        private NameValueCollection _params = null;

        public MobileService()
        {
            _params = null;
            if (HttpContext.Current.Request.HttpMethod == "POST")
            {
                _params = HttpContext.Current.Request.Params;
            }
        }

        public Response ListBusinesses(Int32 intBusinessType)
        {
            return new Response(AcknowledgmentStateSuccess, ConvertToString(Business.List(intBusinessType)));
        }

Step 3: Create a DataContract for a generic response:

[DataContract]
    public class Response
    {
        private AcknowledgmentState _ack;//custom types
        private List _errors;
        private Object _result;

        private void Initialize(AcknowledgmentState ack, List errors, object res)
        {
            _ack = ack;
            _errors = (errors == null) ? new List() : errors;
            _result = (res == null) ? "" : res;
        }

        public Response()
        {
            Initialize(AcknowledgmentStateCustom, null, null);
        }

        public Response(object res)
        {
            Initialize(AcknowledgmentStateSuccess, null, res);
        }

        public Response(AcknowledgmentState ack, object res)
        {
            Initialize(ack, null, res);
        }

        public Response(AcknowledgmentState ack, Error error, object res)
        {
            List errors = new List(1);
            errors.Add(error);
            Initialize(ack, errors, res);
        }

        public Response(AcknowledgmentState ack, List errors, object res)
        {
            Initialize(ack, errors, res);
        }

        [DataMember(EmitDefaultValue = true, IsRequired = true)]
        public AcknowledgmentState Ack { get { return _ack; } set { _ack = value; } }

        [DataMember(EmitDefaultValue = false, IsRequired = false)]
        public List Errors { get { return _errors; } set { _errors = value; } }

        [DataMember(EmitDefaultValue = false, IsRequired = true)]
        public Object Result { get { return _result; } set { _result = value; } }
    }

Step 4: Create a business class that takes in the type of business, and respond with a list.



Step 5: Configure and then use the App_Browser, MOBILE.BROWSER class file

private static List List(int? businessType, int count)
        {
            List businessList = new List();

            if (count > 100)
                count = 100;

            EnumBusinessType enumBizType = EnumBusinessType.UNKNOWN;
            try
            {
                if(businessType != null)
                    if(businessType > 0)
                        enumBizType = (EnumBusinessType)Enum.ToObject(typeof(EnumBusinessType), businessType);
            }
            catch { }

            using (NorthwindDataContext dc = new NorthwindDataContext())
            {
                var records = dc.Businesses.Select(b=> b);
                
                if(enumBizType != EnumBusinessType.UNKNOWN)
                    records = records.Where(b=> b.TypeID == businessType);

                if (!string.IsNullOrEmpty(prefix))
                    records = records.Where(b => b.Name.StartsWith(prefix.Trim()));

                records = records
                            .OrderBy(b => b.Name)
                            .Take(count);

                foreach (var rec in records)
                {
                    businessList.Add(new MyBusinessContract(rec.ID, rec.Name));
                }
            }
            return businessList;
        }


Note that App_Browser is a feature that lets you *tweak* the way user interface is rendered based upon different devices. Its an xml based file, that uses xml to format the user interface presentation.

From MSDN:
Browser definition files contain definitions for individual browsers. At run time, ASP.NET uses the information in the request header to determine what type of browser has made the request. Then ASP.NET uses .browser files to determine the capabilities of the browser. ASP.NET control adapters can use this information to adapt the behavior of an ASP.NET Web server control depending on the type of device. For example, a server control might generate different HTML for a graphical browser such as Internet Explorer than it would for a mobile device.
I will add this .SLN on codeplex soon, ping me if I don’t. (0:

SOLUTION 2: WCF RESTful service with customized client applications




This means, separate client apps for separate devices, for instance Objective-C based app for iPhone, Silverlight based app for Windows Mobile. In this case, let’s get inside the core, and come up with a set of core functionality.

Lets decide, what the "Core" is? Or what the Core should be - what will be the functionality that Core will perform. Ideally, when we say Core, it essentially means, a layer that performs basic or atomic operations.

Over that Core you can wrap the device wrapper (IPhone, PC, etc) that builds a query to be sent to core, for instance, coming in from different device channels.
 
We can use WCF Data Service to constitute a data layer. And your core can perform business operations/etc, and communicate with the Data Service.

For instance:
1. Our core will performs core operations - for instance talking to Datalayer ; the Core will be a "library" that will perform basic operations:
  • Authentication
  • Authorization
  • Select records
  • Perform Action 1
  • Perform Action 2
2. A DeviceWrapper(DeviceAndriod, DeviceIPhone, DeviceWeb, etc), that wraps different devices.
The first thing that comes to mind, in this case, is a mixture of Factory and Builder pattern. Think about factory of Devices, and Builder pattern for types of views to be rendered on different devices.

3. TextRenderer, based upon the device settings, renders the text. Therefore, following will be the steps to implement:
  • Step 1: Write a core that will provide authenticate/authorization; for instance, call the Core.Authenticate(), and .Authorize();
  • Step 2: Based upon the user agent call ICore.RenderView(agentType, url), this may return you the view/html to be rendered.
User may perform an action, your request may call Core.PerformAction(); which in turn may return the link to the page to be redirected, for instance.

Up till now, we were talking about Server side, which primarily is concerned with:
  • How the requests from different user agents will be processed by the server.
  • How the text will be rendered with respect to different devices.
Client ends could be:
  1. A web browser, in this case you don't need any client app to be developed –OR–
  2. A, let’s say, Silverlight app for Windows Phone –OR–
  3. An android app for Android phone –OR–
  4. So on so forth…
Decide what this "client app" will do. For instance:
  • User shall login
  • User shall be able to perform certain operations
Note that, these client apps will call the server side smartly (using web services, rather than asp.net web pages) in order to perform operations.