Enterprise Application FrameworX
Enterprise Application FrameworX
   Home    |   Sign In    |   Register    |   Site Map   
 
   Architecture
     OO Coding Standards - C#, Java...
     Construction - Essential Concepts
     Software Development Life Cycle
   IT Strategy Consulting
   Software Solutions
   Networking Solutions
   Web Development
   Terms and Conditions
     Anti Spam Policy
   Contact Us
   Products
     Ebay
 
      

Construction - Essential Concepts


This page outlines the essential aspects of software construction that every developer has to keep in mind while constructing applications. The goal here is to keep in mind that you are constructing an Application Framework, not just an application. This thought will go a log way.

It is recommended that the reader should read the Software Development Life Cycle article first, before delving into this page

(1) Reusability

Reusability is achieved by having a single implementation base of common functionality, whether it is at the platform layer, or at the business process layer.

Common tasks like Encryption, Transport, Logging, Database Access, Security, etc need to be uniform across the enterprise in order to be useful. The right place for these components is an Enterprise Class Library that is available to all developers. The use of an Enterprise Class Library (ECL) that incorporates these features should be made Mandatory, so every developer does not have to come up with his own implementation and strategy for these. Explore and identify the existence of an Enterprise Class Library. If there isn’t one, inform your manager and collaborate with other developers to come up with one. If there is an ECL that already exists then get a copy of it on your computer and explore it. If you find functionality that is inadequate or missing, you should incorporate this into the ECL so not only you, but the entire team can use it.

Cross Platform ECL will require some extra work and wrapping if you work across a multitude of platforms, but that is where Service Oriented Architecture (SOA) steps in. The goal is to drive Business Process Productivity instead of spending time in Platform Engineering, or re-engineering the same functionality on multiple platforms. For example, an Asynchronous logging routine written in C# can be encapsulated in a fire and forget Web Service, which uses the Logging ECL. Now we could implement downloadable stubs for C, C++ and Java applications that could then implement logging without having to re-invent the wheel. Another benefit of this approach is that logging becomes centralized.

(2) Componentization

Componentization is achieved not only through an ECL, but different parts of the Business Processing Framework can be componentized as well. This enables implementation of specific entity-driven or process-driven functionality in one place.
For example, both the desktop store clerk application and the e-commerce web site may require the use of a Customer class with a (overloaded) Search function that enables the applications to retrieve a customer by a combination first name, last name, email, member number, Date Of Birth, phone number or even item last purchased. By implementing the Customer Class in a Business Class Library (BCL), we can enable Developer A and Developer B working on the same use case in multiple applications to share one implementation. Of course, this would also mean that when Impact analysis is done on the BCL, changes might need to be propagated to both applications. But that cost has almost always proven to be lower than the cost of maintaining redundant implementations.

(3) Scalability

Scalability in a platform is defined as the ability of the platform to handle an increased number of user requests and users themselves, (Throughput, in simple words) without adverse degradation in functionality. There are two ways to scale a platform.

Scaling Up is defined as the ability to increase Hardware or Software parameters in the system configuration to enable more throughput – this includes activities like increasing the software configuration for thread count or number of users, changing the network link to higher speeds or changing the hardware configuration by adding more resources in terms of Memory, Processor Speed, Number of Processors, Hard Drive Space, Hard Drive Speed, etc.

Scaling Out is quite a different animal. You can scale out by making copies of your implementation and directing user traffic, and user request traffic to these multiple copies. Many techniques exist for this – Replication, Web Farming, DNS Round Robin, Network Load Balancing (NLB), Server Load Balancing, Quota Management, etc. You will have to do independent research to understand each of these, but the point of bringing this up here is that in order for your application to support scaling out, it has to be designed in a particular way. For example, web servers that maintain a user session cannot afford to direct user requests within a session to a different server in the farm. This is where you need to come up with a loosely coupled architecture where session data is stored in the Database instead of the web server. This would also mean that the user needs to include a unique session identifier in each request. These and many more are the reasons why scalability planning requires a lot of thinking ahead and planning.

(4) Visibility

Logging is Key to obtaining visibility into the application’s performance. Every application should log in order to provide visibility into Application Execution.

Logging should be configurable at various levels – Informational, Warning, Errors, Success Audits and Failure Audits. It is recommended to use an ECL while logging from within applications, even across platforms. While it is ideal to log to the Windows event log or a database table, logging to a Text File is acceptable under exceptional circumstances. Understand that file I/O happens in all cases, however, directly logging to a file is an extremely single threaded activity.

In any event, the Fire-and Forget Model (Asynchronous Logging) should be utilized or Logging will impede the primary thread of execution. The Enterprise Instrumentation Framework (EIF) in Windows has a Fire-And-Forget Event Logging Model. A good example is the EventLog.WriteEntry method in the .NET Framework. An application can initiate numerous logging requests in multiple threads. All the requests are queued and control returns to the primary invoking thread almost instantaneously, ensuring that logging will happen, but in a lower priority thread, thus ensuring performance.

In any case, Logging does otherwise have a performance impact. Therefore, informational message logging should only happen when the application is run in Debug mode during troubleshooting. Logging to a Database is also permitted, as long as it is not performance intense (Append only – No Key Lookup) and asynchronous.

Documentation is also key to visibility. It should be mandatory for applications to use standard, well-defined error codes on a per-application basis. These should be documented centrally and made available to the Production Support team as it will aid them in both identifying the error and route the corrective action accordingly.

(5) Extensibility

Extensibility is defined as the feature of an Application or Module to accommodate increased functionality with minimal impact to the existing codebase or functionality, and with increased re-usability.

Extensibility depends directly on Modularity. Code should be designed in the form of individual blocks, each of which performs its distinct atomic function. This simply means that we should refrain from writing long procedural blocks of code that execute a complex sequence with a lot of branching, looping and especially control transfer. Instead, it should be broken into a set of individual method invocations. That way, the code is readable and broken down into segments and if there is a need to accommodate another step, or different functionality, it is possible to do so by injecting another method call and adding a new method as opposed to injecting a block of code to a pre-existing block of code.

In addition to modularity, Inheritance is critical to sharing code across a set of classes without having to incorporate the shared code in each module. One should implement functionality that is shared by numerous classes in a base class. It may not be possible for every class to have multiple base classes, in which case using of strongly typed interfaces is recommended.

For example, if we want each screen in our application to support hibernation – the ability to persist the application to disk and take off at any point, it becomes critical that the Screen classes (Forms) in the application implement an interface that contains a Serialize method (like ISerializable) which forces every screen to implement the Serialize method, thus enforcing the ability to persist to disk. It may be impossible to ensure that the implementation of serialization in our example is going to work. The only way to ensure that is to govern the Unit test cases and code itself using the enforced SDLC Steps as we outlined earlier in this document.

We shall illustrate this with a real life example. Let us say that we have a Customer class in our application which implements a Search() method that returns an array of CustomerDetail class instances based on valid search criteria. Now imagine that you have extended your application to also enable search in a business partner's database. Example being the automotive industry. Your web site currently services GM customers. Now, all of a sudden BMW and Mercedes want you to handle their customers also. You would create a generic Customer base class and create three specialized classes - GMCustomer, BMWCustomer and MercedesCustomer, all three implementing the Search() method against the specific database. Again BMW may not want to share the data with you, but might want you to access their web service instead, while GM offers a data feed into your table. One Behavior - Multiple implementations. With inheritance, you can make this happen. Your application can continue using a reference to the Customer class and not have to worry about the actual implementation (extension).

The above example also illustrates Polymorphism - The ability to implement multiple implementations of code and enable as single invocation or instantiation to utilize different implementations based on context. Polymorphism is illustrated in two manners in the above class example.

Instantiation-driven: For one, depending on the system context during construction, the application can use a reference to the Customer class to either instantiate an instance of the BMWCustomer, or an instance of the GMCustomer class. This means that results can be obtained from a specific system with a different search implementation based on system.

Note that you can also model classes to use these different implementations of the Customer class to aggregate the results of the customer search. What if your search screen wanted to search across systems? This aggregation could not be achieved without the use of a Factory Class or a specific CustomerSearch class, which in turn invoked the search on each of the individual implementations and returned the aggregated result in the form of an array of references of type CustomerDetails.

Invocation-Driven: Understand that the Search method itself can contain several specific implementations depending on the order and type of arguments used - Method overloading. It is obvious that the valid return type for all these search implementations should be an array of references to the type CustomerDetails, but depending on the arguments, different method implementations will be invoked. This is the second type of polymorphism that the above example demonstrates.

While there are multiple implementations of Customer, and there are multiple ways to implement this search, based on parameters, some criteria remain common to all the implementations by class and by method. This is where inheritance promotes reusability. You can implement common functionality like connecting to the database (ODBC), retrieving the connection string, decrypting data, etc into the base class Customer, thus enabling every implementation of the class to not worry about implementing the base features. This can greatly increase productivity.

(6) Data Access Strategy

Isolation: You could use technologies like LINQ to loosely couple your implementation with the actual database implementation, but there is a cost associated with this level of isolation. The reasoning behind the approach of refraining from implementing extensive logic in stored procedures, especially triggers at the database layer is that doing so greatly reduces visibility to program logic in the code thus making the task of understanding logic a two-tiered task.

Abstraction: A better strategy is Abstraction. Irrespective of database, certian functionality is very common to across all databases. Methods like CheckDatabaseConnection(), RetrieveDatabaseQueryString(), ConnectToDatabase(), ExecuteQuery(), ExecuteScalar(), ExecuteSqlStatement() are used by applications irrespective of the database. This is where it makes great sense to use a Database Access Layer (DAL) component that ensures that the database connectivity is always happening through a centralized unit of code. Therefore, if there is ever a need to make changes to the database integration, for example, if you wanted to log the time before invocation and after result delivery to see if database access is a bottleneck this could be implemented in the DAL component instead of changing every block of code in your application that accesses the database.
Powered by Eafx Portal FrameworX ©Eafx Inc 1999-2009, All rights reserved.