Showing posts with label System Architecture. Show all posts
Showing posts with label System Architecture. Show all posts

Wednesday, October 14, 2009

Not ready for Inversion Of Control, just use a factory for now

There has been a lot of discussion latterly in the blog world about using inversion of control or IOC, some claiming it’s to hard to understand, it complicates development, and it’s unnecessary; the other side saying quit complaining it’s not that hard, it enforces good design and makes code more maintainable. To this I’m going to take the vary controversial position that they are both right.

Inversion of control can be intimidation the first time you use it, it adds a bit of complexity and you can write quality applications with out it. On the other side once you “get it” IOC really isn't that hard it encourages separation of responsibilities, and makes it much easier to test making regression testing and refactoring much easier.

So how do we get all of the advantages with out adding all of the complexities of an IOC container, simple just use a software factory class. For those who don’t know, basically a factory class is just away to abstract and consolidate the logic for getting your code dependencies.

Let’s say you have some code for accessing data from Amazon’s simpleDB

   1: public class ContactData : IContactData
   2: {
   3:     private Proxy simpleDBProxy;
   4:     public Proxy SimpleDBProxy
   5:     {
   6:         get
   7:         {
   8:             if (simpleDBProxy==null)
   9:             {
  10:                 simpleDBProxy = new Proxy();
  11:             }
  12:             return simpleDBProxy;
  13:         }
  14:         set
  15:         {
  16:             simpleDBProxy = value;
  17:         }
  18:     }
  19:     const string DomainName = "Contacts";
  20:  
  21:     public Contacts GetContacts()
  22:     {
  23:         Contacts myContacts = new Contacts();
  24:  
  25:         SelectRequest request = new SelectRequest
  26:         {
  27:             SelectExpression = string.Format("SELECT * FROM {0} ", DomainName)
  28:         };
  29:         SelectResponse response = SimpleDBProxy.Service.Select(request);
  30:  
  31:         var contacts = from item in response.SelectResult.Item
  32:                          select new Contact()
  33:                                     {
  34:                                         Email = item.Attribute.GetValueByName("Email"),
  35:                                         Name = item.Attribute.GetValueByName("Name"),
  36:                                         Phone = item.Attribute.GetValueByName("Phone"),
  37:                                         ID =  item.Name
  38:                                     };
  39:         myContacts.AddRange(contacts);
  40:         return myContacts;
  41:     }
  42:  
  43:     public Contacts GetContactsByName(string contactName)
  44:     {
  45:         Contacts myContacts = new Contacts();
  46:  
  47:         SelectRequest request = new SelectRequest
  48:         {
  49:             SelectExpression = string.Format("SELECT * FROM {0} where Name='{1}' ", DomainName, contactName)
  50:         };
  51:         SelectResponse response = SimpleDBProxy.Service.Select(request);
  52:  
  53:         var contacts = from item in response.SelectResult.Item
  54:                        select new Contact()
  55:                        {
  56:                            Email = item.Attribute.GetValueByName("Email"),
  57:                            Name = item.Attribute.GetValueByName("Name"),
  58:                            Phone = item.Attribute.GetValueByName("Phone"),
  59:                            ID = item.Name
  60:                        };
  61:         myContacts.AddRange(contacts);
  62:         return myContacts;
  63:     }
  64:    
  65:     public bool SaveContact(Contact contact)
  66:     {
  67:         List<ReplaceableAttribute> attributeList = new List<ReplaceableAttribute>
  68:            {
  69:                new ReplaceableAttribute().WithName("Email").WithValue(contact.Email),
  70:                new ReplaceableAttribute().WithName("Name").WithValue(contact.Name),
  71:                new ReplaceableAttribute().WithName("Phone").WithValue(contact.Phone)
  72:            };
  73:         contact.ID = Guid.NewGuid().ToString();
  74:         bool success = false;
  75:         try
  76:         {
  77:             if (!SimpleDBProxy.Domains.Contains(DomainName))
  78:             {
  79:                 SimpleDBProxy.AddDomain(DomainName);
  80:             }
  81:             PutAttributesRequest action = new PutAttributesRequest
  82:               {
  83:                   ItemName = contact.ID,
  84:                   Attribute = attributeList,
  85:                   DomainName = DomainName
  86:               };
  87:             PutAttributesResponse response = SimpleDBProxy.Service.PutAttributes(action);
  88:             success = true;
  89:         }
  90:         catch (Exception requestException)
  91:         {
  92:             success = false;
  93:         }
  94:  
  95:         return success;
  96:     }
  97: }

now because this implements the IContactData interface

   1: public interface IContactData
   2: {
   3:     Contacts GetContacts();
   4:     Contacts GetContactsByName(string contactName);
   5:     bool SaveContact(Contact contact);
   6: }

we can use it anywhere we would use IContactData, for example here we have an application that uses both local and remote contacts

   1: public class ContactsRequests
   2: {
   3:     private IContactData ContactData;
   4:     public bool UseLocalContacts { get;set; }
   5:  
   6:     public Contacts SearchContactsByName(string contactName)
   7:     {
   8:         if (UseLocalContacts)
   9:         {
  10:             ContactData = new LocalContactData();
  11:         }
  12:         else
  13:         {
  14:             ContactData = new ContactData();
  15:         }
  16:         return ContactData.GetContactsByName(contactName);
  17:     }
  18:  
  19:     public Contacts GetContacts()
  20:     {
  21:         if (UseLocalContacts)
  22:         {
  23:             ContactData = new LocalContactData();
  24:         }
  25:         else
  26:         {
  27:             ContactData = new ContactData();
  28:         }
  29:         return ContactData.GetContacts();
  30:     }
  31:  
  32:     public bool AddContact(Contact newContact)
  33:     {
  34:         return ContactData.SaveContact(newContact);
  35:     }
  36: }

now this works but there are a few problems, first there is no way to test it’s functionality without testing the functionality of LocalContactData and/or ContactData with a little simple refactoring

   1: public class ContactsRequests
   2: {
   3:     private IContactData contactData;
   4:     public IContactData ContactData
   5:     {
   6:         get
   7:         {
   8:             if (contactData==null)
   9:             {
  10:                 if (UseLocalContacts)
  11:                 {
  12:                     contactData = new LocalContactData();
  13:                 }
  14:                 else
  15:                 {
  16:                     contactData = new ContactData();
  17:                 }
  18:             }
  19:             return contactData;
  20:         }
  21:         set
  22:         {
  23:             contactData = value;
  24:         }
  25:     }
  26:     public bool UseLocalContacts { get;set; }
  27:  
  28:     public Contacts SearchContactsByName(string contactName)
  29:     {          
  30:         return ContactData.GetContactsByName(contactName);
  31:     }
  32:  
  33:     public Contacts GetContacts()
  34:     {
  35:        return ContactData.GetContacts();
  36:     }
  37:  
  38:     public bool AddContact(Contact newContact)
  39:     {
  40:         return ContactData.SaveContact(newContact);
  41:     }
  42: }
we remove the instantiation logic from the method and move it to a public property this way we can set the ContactData property to a mock object and test the specific logic of the methods in this class, now the only problem with this is everywhere we want to use IContactData we have to perform the same logic, and basically reproduce the same code over and over again. This is where factories come in, if we build a factory like this
   1: public static class DataFactories
   2: {
   3:     public static IContactData GetContactInterface(bool isLocal)
   4:     {
   5:         IContactData myContactData;
   6:         if (isLocal)
   7:         {
   8:             myContactData = new LocalContactData();
   9:         }
  10:         else
  11:         {
  12:             myContactData= new ContactData();
  13:         }
  14:         return myContactData;
  15:     }
  16: }

Then we just implement it like this

   1: public IContactData ContactData
   2: {
   3:     get
   4:     {
   5:         if (contactData==null)
   6:         {
   7:             contactData = DataFactories.GetContactInterface(UseLocalContacts);
   8:         }
   9:         return contactData;
  10:     }
  11:     set
  12:     {
  13:         contactData = value;
  14:     }
  15: }

your applications don’t know or care how they got there class for getting contact all they know is they did, where this really comes in handy is if you have to change out how you handle your local contacts from let’s say an xml file to a database, the only place you have to change your code is in the factory.

Congratulations you are now 90% of the way there for using a IOC container, you have a single place for handling all of your instantiation so once you decide to use IOC you just change how the factory works from having logic that decides what class to insatiate to pulling it from the IOC container, it’s that simple.

Truth be told I always start with a factory and then later “IF” I need the functionality of an IOC I’ll add it. In the end an IOC container really isn’t nearly as scary as it seems to be, and in the same token it isn’t nearly a vital as some people would like to claim, it’s a tool that can be used to solve specific problems, but 90% of the same problems can be solved using a factory.


Tuesday, August 25, 2009

N-Tier Design Revisit part 5 – UI Layer

Finally we come to the User Interface Layer, logically this can be the simplest part of your application with something like a web service that simply exposes a set of APIs calls to your business layer or by far the most complex part of your application with something like a multi-threaded winform.

The most important thing to remember is just like every other layer, the UI layer ONLY contains UI logic:

  1. Navigation: One of the most important and visible parts of your application, it doesn't matter how good your content is, if you can’t navigate through it. The basic rules are: Keep it simple, Intuitive, and Consistent if your customers have to think about how to navigate, there is a problem.

  2. Event Handling: The basic process of taking input from the customer and responding to it appropriately.

  3. Application State: Keeping track of where the user is and what they are doing can be very complex or fairly simple depending on the type application your working on.

  4. Displaying: This is basically everything the the user sees from controls to the application output and has the same basic rules as Navigation for the same reasons, if your application is hard to use, your users wont use it.

The most important thing to remember is to keep any logic that isn’t specific to the UI out of the UI, a good example is validation. If you put your validation in the UI you then have to maintain your validation logic in multiple places(asp.net application, WinForm, Web service, etc). and can lead to inconsistent validation between your applications, Save yourself the pain and put a validation in the biz layer. There are some small exceptions for web development when using client side validation, just remember to NEVER trust input from the client, this goes double for web clients, to quote Tony Rose “The Client is in the hands of The Enemy.” and should never be trusted, it’s too easy to have javascript errors that circumvent your validation.

Technorati Tags: ,,,

Friday, July 17, 2009

N-Tier Design Revisit part 1 – Over View

Back in March I did the blog entry The value of N-Tier Design, I didn’t provide any code, it was just a simple here is why N-Tier is good. After some reflection I have decided to revisit the subject and go a little more into detail.

The primary focuses of N-Tier is to provide a separation of concerns that will make your code more testable and more importantly maintainable, remember the code you write today, is the code you maintain tomorrow. Most of what I’m going to cover are some simple suggestions to make your life easier by putting in a little extra work up front, they may not be applicable in all situations, like a small one time use app for parsing a log file, some good rules of thumb are, if you can rewrite the application in less then 4 hours or it’s a proof of concept for a specific technology, it’s a little over kill for that.

The basic layers of N-Tier are

  1. UI Layer – Contains the logic for interacting with your user, this could be a web form, web service, command-line application, Winform, etc.
  2. Biz Layer – Contains business logic like data validation, manipulation, etc.  Basically any logic that deals with business rules belongs in the Biz
  3. Data Access Layer – Getting the Data and mapping it to Entities.  this could be from a Data Base, a 3rd party web service, an xml File, etc.
  4. Data Entities – Are not a layer, but how data is passed from one layer to another

Layer can consist of a single class or a set of classes that perform different tasks as long as they are doing what that layer is tasked with.

The basic rules for N-Tier are simple

  1. Layers can only talk to the layer right next to it, the UI layer can only talk to the Biz layer, the Biz can talk to the UI layer and the Data Access but the Data Access layer can only talk to the Biz Layer
  2. A Layer should have no internal knowledge of any of the other layers, A Biz class should have no understanding of how the DAL gets the data that it gives the it, The data could come from a Database, an XML File, or be randomly generated. Getting the data isn’t the Biz layers job so it shouldn’t care where it comes from, this becomes more important when you start using dependency injection and the underlying code may change completely.
  3. The layers should be decoupled and use interfaces to communicate with each other, this makes it easier to swap out code, use dependency injection , etc. you should be able to completely change a layer and as long as it keeps the same interface.

Sunday, July 12, 2009

Using WMI for building better applications

There was a couple of stack overflow question the other day that got my attention: How to find out if a user has administer privileges? and How to programmatically discover mapped network drives on system and their server names? My fist thought was WMI, and it got me thinking about how little WMI is discussed, I’ve never seen a conference session about it, or a session at code camp on it. Yet if your doing any serious system development it’s almost required.

For starters what is WMI?

According to Microsoft Technet “Windows Management Instrumentation (WMI) is the primary management technology for Microsoft® Windows® operating systems. It enables consistent and uniform management, control, and monitoring of systems throughout your enterprise….” see Microsoft Technet

So what dose this mean for a .NET developer?

You can access WMI though the System.Management namespace and can be used for finding out just about anything you want to know about your computer and Microsoft network. For example what drives do you have mapped?

   1: using System;
   2: using System.Management;
   3: using System.Windows.Forms;
   4:  
   5: namespace WMISample
   6: {
   7:     public class MyWMIQuery
   8:     {
   9:         public static void Main()
  10:         {
  11:             try
  12:             {
  13:                 ManagementObjectSearcher searcher = 
  14:                     new ManagementObjectSearcher("root\\CIMV2", 
  15:                     "SELECT * FROM Win32_MappedLogicalDisk"); 
  16:  
  17:                 foreach (ManagementObject queryObj in searcher.Get())
  18:                 {
  19:                     Console.WriteLine("-----------------------------------");
  20:                     Console.WriteLine("Win32_MappedLogicalDisk instance");
  21:                     Console.WriteLine("-----------------------------------");
  22:                     Console.WriteLine("Access: {0}", queryObj["Access"]);
  23:                     Console.WriteLine("Availability: {0}", queryObj["Availability"]);
  24:                     Console.WriteLine("BlockSize: {0}", queryObj["BlockSize"]);
  25:                     Console.WriteLine("Caption: {0}", queryObj["Caption"]);
  26:                     Console.WriteLine("Compressed: {0}", queryObj["Compressed"]);
  27:                     Console.WriteLine("ConfigManagerErrorCode: {0}", queryObj["ConfigManagerErrorCode"]);
  28:                     Console.WriteLine("ConfigManagerUserConfig: {0}", queryObj["ConfigManagerUserConfig"]);
  29:                     Console.WriteLine("CreationClassName: {0}", queryObj["CreationClassName"]);
  30:                     Console.WriteLine("Description: {0}", queryObj["Description"]);
  31:                     Console.WriteLine("DeviceID: {0}", queryObj["DeviceID"]);
  32:                     Console.WriteLine("ErrorCleared: {0}", queryObj["ErrorCleared"]);
  33:                     Console.WriteLine("ErrorDescription: {0}", queryObj["ErrorDescription"]);
  34:                     Console.WriteLine("ErrorMethodology: {0}", queryObj["ErrorMethodology"]);
  35:                     Console.WriteLine("FileSystem: {0}", queryObj["FileSystem"]);
  36:                     Console.WriteLine("FreeSpace: {0}", queryObj["FreeSpace"]);
  37:                     Console.WriteLine("InstallDate: {0}", queryObj["InstallDate"]);
  38:                     Console.WriteLine("LastErrorCode: {0}", queryObj["LastErrorCode"]);
  39:                     Console.WriteLine("MaximumComponentLength: {0}", queryObj["MaximumComponentLength"]);
  40:                     Console.WriteLine("Name: {0}", queryObj["Name"]);
  41:                     Console.WriteLine("NumberOfBlocks: {0}", queryObj["NumberOfBlocks"]);
  42:                     Console.WriteLine("PNPDeviceID: {0}", queryObj["PNPDeviceID"]);
  43:  
  44:                     if(queryObj["PowerManagementCapabilities"] == null)
  45:                         Console.WriteLine("PowerManagementCapabilities: {0}", queryObj["PowerManagementCapabilities"]);
  46:                     else
  47:                     {
  48:                         UInt16[] arrPowerManagementCapabilities = (UInt16[])(queryObj["PowerManagementCapabilities"]);
  49:                         foreach (UInt16 arrValue in arrPowerManagementCapabilities)
  50:                         {
  51:                             Console.WriteLine("PowerManagementCapabilities: {0}", arrValue);
  52:                         }
  53:                     }
  54:                     Console.WriteLine("PowerManagementSupported: {0}", queryObj["PowerManagementSupported"]);
  55:                     Console.WriteLine("ProviderName: {0}", queryObj["ProviderName"]);
  56:                     Console.WriteLine("Purpose: {0}", queryObj["Purpose"]);
  57:                     Console.WriteLine("QuotasDisabled: {0}", queryObj["QuotasDisabled"]);
  58:                     Console.WriteLine("QuotasIncomplete: {0}", queryObj["QuotasIncomplete"]);
  59:                     Console.WriteLine("QuotasRebuilding: {0}", queryObj["QuotasRebuilding"]);
  60:                     Console.WriteLine("SessionID: {0}", queryObj["SessionID"]);
  61:                     Console.WriteLine("Size: {0}", queryObj["Size"]);
  62:                     Console.WriteLine("Status: {0}", queryObj["Status"]);
  63:                     Console.WriteLine("StatusInfo: {0}", queryObj["StatusInfo"]);
  64:                     Console.WriteLine("SupportsDiskQuotas: {0}", queryObj["SupportsDiskQuotas"]);
  65:                     Console.WriteLine("SupportsFileBasedCompression: {0}", queryObj["SupportsFileBasedCompression"]);
  66:                     Console.WriteLine("SystemCreationClassName: {0}", queryObj["SystemCreationClassName"]);
  67:                     Console.WriteLine("SystemName: {0}", queryObj["SystemName"]);
  68:                     Console.WriteLine("VolumeName: {0}", queryObj["VolumeName"]);
  69:                     Console.WriteLine("VolumeSerialNumber: {0}", queryObj["VolumeSerialNumber"]);
  70:                 }
  71:             }
  72:             catch (ManagementException e)
  73:             {
  74:                 MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
  75:             }
  76:         }
  77:     }
  78: }

for a lot of developers this doesn't mater one way or another, for most winform or WPF apps you don’t need it, if your doing web development their is almost never going to be a reason to touch WMI.

One exception to this is if your working on an intranet application. How many time have you seen internal application that use a database to manage employees, there roles, etc. and then manage there active directory separately, why not just use active directory to do both?

If this seems a little obscure don’t worry there is a tool to do most of the heavy lifting for you.

The WMI Code Creator v1.0

WMI

It builds WMI queries, execute them, browse the local computers namespace, etc. for more information see the product overview.