Different Object Models in a distributed applications

In a distributed application, we might have different layers deployed in different physical machines and there would be well-defined responsibilities for each layer, communication between the layers using a standard protocol/interface.

Main intent of moving a layer to a different location would be 1) Security (sensitive data store/business logic behind firewall)  2) Scalability 3) Integration with other applications etc.

Assuming that a distributed application has three layers:- 1) UI/Presentation Layer, 2) Business Layer 3) Data Access Layers, I believe that application would have the following object models 1) View Model, 2) Domain Model 3) Data Model (Conceptual Object Models) 4) Object Model for communication between different layers(in different physical locations).

The purpose of all these Object models would be different. 

I believe the purpose of View Model is to provide an efficient way of binding UI Controls with the appropriate data and to collect the data from the user , validate it and pass it on to next layer.

I believe the purpose of Domain Model is to organize the data and processing code in the BLL more meaning full way. All objects in this model represents tangible real-world objects (The purpose of any software system I believe is to automate a manual business process and Domain model can be derived from the real-world entities that will be involved in that manual process).

 And database conception model purpose is to efficiently retrieve data from physical data store, manipulate data efficiently in the physical data store. The design of database schema will be done considering efficiency of storage, scalability, performance of data access not based on UI or BLL requirements. So the conceptual model of data might not completely resemble the Domain Model.

A Data Model might have all the traits of Domain Model like : Repositories, Services, Aggregates, Entities, Value Objects and Bounding Contexts. But the purpose of Data Model is quite different from Domain Model.

In most of the two-tier applications, Data Model and  Domain Model will be the same. But it is recommended to have a separate Domain Model containing objects with domain specific behavior.

And Object Model for communication between layers(DTO's, messages etc) focuses on secure , reliable marshaling and transferring of data between layers.

Since the model in each layer are related but might not be exactly same,  we need a mapping mechanism for
  •       Physical data store                   <===>   Conceptual Data Model
  •       Data Model                               <===>   Transfer Model (Not always required)
  •       Domain Model                          <===>  Data Model
  •       Domain Model                          <====> Transfer Model
  •      Transfer Object Model             <====> View Model
       

Going Abstract with Functional Programming

Traditional Object Oriented programming developers(like me), are used to programming in the imperative style. More emphasis was on designing class hierarchies, algorithms ( sequence of commands/statements to perform a unit of operation), tracking changes in the state, control flow.

Writing code , describing the details in each statement/command, tracking the state changes, creating CFG(Control flow Graphs) to analyze all possible flows(order of execution), creating unit tests so that maximum code-coverage is accomplished etc were the major tasks of an OO programmer.

But for enhancing/maintaining a functionality OO programmer should again track the control flow, state changes of local variables and global variables due to hidden dependencies etc. Enhancing/Extending functionality is not as predictable as it seems to be.

In imperative programming, same design patterns are written over and over again. 
Expressing what you wanted to achieve is very difficult in imperative programming (even in OO languages). 

It takes time for a developer to go through a code snippet and understand the intent, since all those sequence of statements express how the code executes and we can figure out what is being accomplished only after careful study of the order of statements , dependencies etc.

Functional Programming:-

  • A style of programming that emphasizes the evaluation of expression rather than execution of commands.
  • Code is an expression that specifies properties of the object we want to get as a result rather than a sequence of statements. 
  • In this declarative approach we compose code differently. 
  • Control flow contains function calls, including recursion.
  • Primary manipulation unit is function. Functions are first-class objects and data collections rather than objects.
 Common Example (In many articles explaining Functional Programming)

C# and Visual Basic support both imperative and functional programming approaches. Started understanding and appreciating the functional programming constructs in C#.

Hope combining both the functional approach and imperative approach improve personal coding productivity as mentioned in many tutorials.

Going Abstract from Concrete

Why should implementation of IUnknown split into several templates in ATL namely CComObjectRootEx<> and CComCoClass, when there is no complexity in implementing IUnknown?

Why should STL have an abstract model of containers, iterators and alogirthms?
             http://www.stroustrup.com/Programming/20_containers.ppt

Why should Enterprise library have a complex abstract object model?
           http://msdn.microsoft.com/en-us/library/ff648712.aspx

Major goals as mentioned in their respective documents : Flexibility , Extensibility , Separation of Concerns , greater possibility of reuse , taking advantage of tested, stabilized and proven libraries and frameworks.

In Agile mode of development, we cannot come up with an abstract model/design for the problem solution(containing multiple business scenarios) we are solving upfront, since we design and implement scenario by scenario. Over doing generalization up-front without proper understanding of concrete implementation, creates complexity and might make the code unmanageable.

Its a challenge to identify when to stop generalization for a problem.

Initially while coming up with Subsystem, identifying the functionality that might vary should be abstracted . Design patterns for abstracting varying functionality and making that code swappable and independently manageable without ripple effects needs to be identified (Strategy, Template patterns, plug-gable framework).

Any pattern implementation satisfies the following principles along with catering to its basic intent.
  • Code to an interface
  • Encapsulate What Varies
  • Only One Reason to Change
  • Classes are about behavior
  • Prefer delegation over inheritance
  • Dependency Inversion Principle

And after having concrete implementations for couple of scenarios, during refactoring again identifying the common functionality that's required for all scenarios and abstracting that functionality into separate classes needs to be done.

"Encapsulate what Varies" Principle in ATL COM (Using Templates)

Object Model



CComXxxThreadModel encapsulates thread safe increment and decrement operation on life-time counter.
class CComSingleThreadModel 
{                                                
  static ULONG WINAPI Increment(LPLONG p) { return ++(*p); }                 
  static ULONG WINAPI Decrement(LPLONG p) { return (*p); }                  
  ...                                                                        
};                                                                           
                                                                             
class CComMultiThreadModel 
{                                                 
  static ULONG WINAPI Increment(LPLONG p) { return InterlockedIncrement(p); }
  static ULONG WINAPI Decrement(LPLONG p) { return InterlockedDecrement(p); }
  ...                                                                        
};
 
And in the implementation of CComObjectRootEx, ThreadModel is passed as class parameter:  


template <class ThreadModel>                                                 
class CComObjectRootEx : public CComObjectRootBase 
{                         
public:                                                                      
    typedef ThreadModel _ThreadModel;                                        
    typedef typename _ThreadModel::AutoCriticalSection _CritSec;             
    typedef typename _ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec;
    typedef CComObjectLockT<_ThreadModel> ObjectLock;
.....
}; 
The static methods in ThreadModel variations, will be used in internal AddRef and Release:-

ULONG InternalAddRef() 
{                                                 
        ATLASSERT(m_dwRef != -1L);                                           
        return _ThreadModel::Increment(&m_dwRef);                            
}                                                                        
ULONG InternalRelease() 
{                                                
#ifdef _DEBUG                                                                
    long nRef = _ThreadModel::Decrement(&m_dwRef);                       
    if (nRef < -(LONG_MAX / 2)) 
    {                                        
            ATLASSERT(0 && _T("Release called on a pointer "                 
                      "that has already been released"));                    
    }                                                                    
    return nRef;                                                         
#else                                                                        
        return _ThreadModel::Decrement(&m_dwRef);                            
#endif                                                                       
}

We might not always have a value add in designing a reusable Framework that encapsulates separately what varies
and core that would be constant . 
To reduce the duplication of boiler plate code, to keep it easily maintainable and extensible we might need to refactor code to ensure 
above principles are taken care of. 

Consider Repository pattern implementation in Domain Driven Design (using C#). Repository is generally used to search a domain object(Root Entities or Entities) in an Aggregate. Repository also provides Persistence Ignorance that abstracts the physical storage code.
    public interface IRepository<T> where T : class
    {    
        T GetById(int id);
        IEnumerable<T> GetAll();
        IEnumerable<T> Query(Expression<Func<T, bool>> filter);        
        void Add(T entity);
        void Remove(T entity);    
    }
In the above interface, T represents any domain object specific to our domain(Account, AccountTransaction etc).

An abstract Repository class , implements common operations for all Domain Aggregates:-
    public abstract class Repository<T> : IRepository<T> where T : class
  •              public IEnumerable<T> GetAll()
  •        public abstract T GetById(int id);
  •        public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
  •        public void Add(T entity)
  •        public void Remove(T entity)
 And each specific repository for specific Aggregate root (Ex:- ShoppingCart) in the Bounded context of (Ex:- Billing) inherits from the abstract base class Repository and provide specific implementation for GetById and any other specific methods for Validation etc:-
         public partial class ShoppingCartRepository : Repository<ShoppingCart>

Cognitive Biases in Software Development

  • Along with the appropriate use of processes ,methodologies and competencies of the developers, right soft-skills of the developers/designers seem to be a key success factor.
  • Soft-Skills in this context are :- Reasoning, identifying the unknowing cognitive biases that might impact our decisions and mitigating them at personal level as well.
         http://www.mendeley.com/catalog/using-traceability-mitigate-cognitive-biases-software-development/#page-1
  • Have a specific premise(assertions) for any decision(conclusion). Decision can be related to development/design/implementation.
     
  • Goals of stakeholders might conflict and no system can satisfy all goals of all stake holders. But as a common goal would be to maximize functionality and minimize cost.
     
  • We should be prepared to accept systems that meet some balance of stakeholder goals. And that balance should be justifiable and acceptable by all stake holders.

     
  • Most of the times due to human limitations, problem solvers cannot evaluate all the rational options to find an optimal solution, instead settle with an acceptable solution.

Start Small (Coding Strategy)

 With regard to the intuition I've developed with my experiences, feedback from my seniors in my past experiences and articles which I've read, I thought of following a strategy which would help my develop activities to be more predictable , easy and fast.
  • Start small, don't aim for technical sophistication (using all the language idioms , patterns and principles) on the first go. 
  • Complete business functionality before making code beautiful. Working business code should be priority  before anything else.
  • We cannot anticipate all future requirements while coming up with architecture of the system, but definitely attempt to create a loosely coupled, layered organization of sub systems so that in future system can be extended without much cost.  
  • After complete understanding the scope of the system and realization of sub-features, try building the In-Memory Business Object Model (Using domain driven design concepts) first in a simple reusable dynamic library or static library or in a unit testing container. Based on the non-functional requirements, based on distribution strategy and based on type of UI (Browser, RAID, Single Page etc) these reusable business logic dlls can be wrapped in appropriate frameworks for communication like COM, COM+, Remoting or Unified framework like WCF for BLL Facade . Data Facade/Gateways can be developed to cater data requirements of BLL.

  • While requirements will be fulfilled by the Design of Domain Model, Architecture ensures the Quality of Services provided by the application.
  • Architectural choices are driven by the following Quality Of Service(QOS) parameters: Performance, Reliability, Availability, Scalability, Security, Flexibility and extensibility.
  • Coming up with architecture of Software system is similar to creating a plan for a house. Architecture guidelines contain rigid rules regarding decomposition of system into sub-systems and communication between them. These rules are driven by non-functional and functional requirements & forces like team size , time to market etc.
  • Building framework for a software application is similar to constructing the overall structure/skeleton (Foundation, Pillars & Columns) for a building. We can change the internal layout of walls, partitions and interior decoration but changing the framework of building would result in lot of rework.
  • Once Architectural guidelines are laid for Software, we need to live with it through out the development. It would be a testing nightmare , if we change the structure/architecture for each build drop.
  • Layering , well established contract between the layers and each layer being cohesive and decoupled improves maintainability, adds orthogonality, allows concurrent development, allows internal evolution(Object model) of each layer without changing the contract.
  • Identify which parts of application are likely to change in future and identify pattern that will help you change it without the rest of application. Shield the rest of the system(core framework) from change.
  • Once architectural guidelines are established and object model of all sub systems and clear responsibilities are realized, start fulfilling the main purpose of each function in classes. 
  • Try developing plain objects with single responsibilities and then use them in frameworks like COM or WCF or MVC. This separation of framework code from actual functionality helps improved testability and  leverages swapping of these frameworks or moving to a new framework later.
  • Use TDD and other development practices which give continuous feedback loops and refactor periodically to cater the non-functional requirements.  
  • API or Frameworks should be developed keeping consumers of that API in mind.
  • TDD process contains the loop :- Code => Test => Fix => Test => Refactor.
  • During refactoring start making small improvement to Code.
  • Objective of each refactoring session is just to make the code better than before. Even changing the function name to an intention revealing name can also be considered as substantial improvement.
  • While refactoring try applying practices like KISS(Keep it Simple Stupid), DRY(Don't Repeat Yourself), YAGNI ( You An't Gonna Need It) , SOC(Separation of Concerns), SOLID etc.
  • Keep the code simple but not simplistic. Simple doesn't mean compromising with functionality. Code should be clear easy to understand should be like well written prose. Should be simple and direct and should be easy to maintain.  
  • For maintainability refactor to simplify the code.
  • Cyclomatic complexity measures the number of unique paths that exist in a method, class or application. Higher cyclomatic complexity number, higher the degree of complexity in the Code.
  • Don't over use frameworks like Dependency Injection Containers that automates and hides the actual dependencies.
  • First establish the dependency contract between different components in a Sub-System and between different sub systems. Then try using Dependency Injection/IOC Containers.
  • During refactoring, ensure each function does one and only one thing.
  • Try explaining most of your intent in code (meaning full names, nicely organized small cohesive, self contained methods with single responsiblity).
  • Apply Law of Demeter. A module should not know about the innards of the objects it manipulates.
  • During refactoring apply DRY principle (Don't repeat Yourself). Abstract the common code and move it to a single location. Ensure one representation for every piece of knowledge in a system.
  • While refactoring, apply "Tell, Dont Ask" principle. Decompose classes in such a way that the consumer of a class tells to object what actions consumer want them to perform rather than asking questions about the state of object and making a decision yourself on what action you want to perform.
  • While refactoring, apply "You Ain't Gonna Need It". Include functionality that is necessary for application. Put off the temptation to add other features that you may think you need.
  • While refactoring, apply SOC(Separation of Concerns). Dissect software into distinct features that encapsulate unique behavior and data that can be used by other classes. 
  • While refactoring , apply SOLID principles. Apply Single Responsibility Principle. Each method or class should have one and only one reason to change.