Tuesday 31 May 2016

Architecture - Key Principles and Practices



- Current thinking on architecture assumes that your design will evolve over time and you cannot know everything you need to know up front in order to fully architect your system.

- Your design will generally need to evolve during the implementation stages of the application as you learn more and as you test the design against real world requirements.

- Create your architecture with this evolution in mind so that it will be able to adapt to requirements that are not full known at the start of the design process.

- Do not attempt to over engineer the architecture and do not make assumptions that you cannot verify. Instead, keep your options open for future change.

-Consider using an incremental and iterative approach to refining your architecture.

Consider the following key principles when designing your architecture.
  •  Build to change instead of building to last. Consider how the application may need to change over time to address new requirements and challenges, and build in the flexibility to support this.
  • Model to analyze and reduce risk. Use design tools, modeling systems such as Unified Modeling Language (UML), and visualizations where appropriate to help you capture requirements and architectural and design decisions, and to analyze their impact. However, do not formalize the model to the extent that it suppresses the capability to iterate and adapt the design easily. 
  • Use models and visualizations as a communication and collaboration tool. Efficient communication of the design, the decisions you make, and ongoing changes to the design, is critical to good architecture. Use models, views, and other visualizations of the architecture to communicate and share your design efficiently with all the stakeholders, and to enable rapid communication of changes to the design.
  •  Identify key engineering decisions. Use the information in this guide to understand the key engineering decisions and the areas where mistakes are most often made. Invest in getting these key decisions right the first time so that the design is more flexible and less likely to be broken by changes.

----------------------------------------------------------------------------------------------------------------------

These key principles with help you to create an architecture that adheres to proven principles, minimizes costs and maintenance requirements, and promotes usability and extendibility.
The key principles are:

  • Separation of concerns.Divide your application into distinct features with as little overlap in functionality as possible. The important factor is minimization of interaction points to achieve high cohesion and low coupling. However, separating functionality at the wrong boundaries can result in high coupling and complexity between features even though the contained functionality within a feature does not significantly overlap.
  • Single Responsibility principle.Each component or module should be responsible for only a specific feature or functionality, or aggregation of cohesive functionality.
  • Principle of Least Knowledge (also known as the Law of Demeter or LoD). A component or object should not know about internal details of other components or objects.
  • Don’t repeat yourself (DRY).You should only need to specify intent in one place. For example, in terms of application design, specific functionality should be implemented in only one component; the functionality should not be duplicated in any other component.
  • Minimize upfront design. Only design what is necessary. In some cases, you may require upfront comprehensive design and testing if the cost of development or a failure in the design is very high. In other cases, especially for agile development, you can avoid big design upfront (BDUF). If your application requirements are unclear, or if there is a possibility of the design evolving over time, avoid making a large design effort prematurely. This principle is sometimes known as YAGNI ("You ain’t gonna need it").
----------------------------------------------------------------------------------------------------------------------

The following high level guidelines will help you to consider the wide range of factors that can affect the ease of designing, implementation, deploying, testing, and maintain your application.

Design Practices
  • Keep design patterns consistent within each layer.
    Within a logical layer, where possible, the design of components should be consistent for a particular operation. For example, if you choose to use the Table Data Gateway pattern to create an object that acts as a gateway to tables or views in a database, you should not include another pattern such as Repository, which uses a different paradigm for accessing data and initializing business entities. However, you may need to use different patterns for tasks in a layer that have a large variation in requirements, such as an application that contains business transaction and reporting functionality.

  • Do not duplicate functionality within an application.
    There should be only one component providing a specific functionality—this functionality should not be duplicated in any other component. This makes your components cohesive and makes it easier to optimize the components if a specific feature or functionality changes. Duplication of functionality within an application can make it difficult  to implement changes, decrease clarity, and introduce potential inconsistencies.
  • Prefer composition to inheritance.Wherever possible, use composition over inheritance when reusing functionality because inheritance increases the dependency between parent and child classes, thereby limiting the reuse of child classes. This also reduces the inheritance hierarchies, which can become very difficult to deal with.
  • Establish  a coding style and naming convention for development.
    Check to see if the organization has established coding style and naming standards. If not, you should establish common standards. This provides a consistent model that makes it easier for team members to review code they did not write, which leads to better maintainability.

  • Maintain system quality using automated QA techniques during development. 
    Use unit testing and other automated Quality Analysis techniques, such as dependency analysis and static code analysis, during development. Define clear behavioral and performance metrics for components and sub-systems, and use automated QA tools during the build process to ensure that local design or implementation decisions do not adversely affect the overall system quality.

  • Consider the operation of your application.
    Determine what metrics and operational data are required by the IT infrastructure to ensure the efficient deployment and operation of your application. Designing your application’s components and sub-systems with a clear understanding of their individual operational requirements will significantly ease overall deployment and operation. Use automated QA tools during development to ensure that the correct operational data is provided by your application’s components and sub-systems.

Application Layers 

  • Separate the areas of concern.
    Break your application into distinct features that overlap in functionality as little as possible. The main benefit of this approach is that a feature or functionality can be optimized independently of other features or functionality. In addition, if one feature fails, it will not cause other features to fail as well, and they can run independently of one another. This approach also helps to make the application easier to understand and design, and facilitates management of complex interdependent systems.
  • Be explicit about how layers  communicate with each other.Allowing every layer in an application to communicate with or have dependencies upon all of the other layers will result in a solution that is more challenging to understand and manage. Make explicit decisions about the dependencies between layers and the data flow between them.
  • Use abstraction to implement loose coupling between layers.
    This can be accomplished by defining interface components such as a façade with well known inputs and outputs that translate requests into a format understood by components within the layer. In addition, you can also use Interface types or abstract base classes to define a common interface or shared abstraction (dependency inversion) that must be implemented by interface components.
  • Do not mix different types of components in the same logical layer.Start by identifying different areas of concern, and then group components associated with each area of concern into logical layers. For example, the UI layer should not contain business processing components, but instead should contain components used to handle user input and process user requests.
  • Keep the data format consistent within a layer or component.
    Mixing data formats will make the application more difficult to implement, extend, and maintain. Every time you need to convert data from one format to another, you are required to implement translation code to perform the operation and incur a processing overhead. Components,Modules, and Functions
  • A component or an object should not rely on internal details of other components or objects.Each component or object should call a method of another object or component, and that method should have information about how to process the request and, if appropriate, how to route it to appropriate sub-components or other components. This helps to create an application that is more maintainable and adaptable.

  • Do not overload the functionality of a component. For example, a UI processing component should not contain data access code or attempt to provide additional functionality. Overloaded components often have many functions and properties providing business functionality mixed with crosscutting functionality such as logging and exception handling. The result is a design that is very error prone and difficult to maintain. Applying the single responsibility and separation of concerns principles will help you to avoid this.
  • Understand how components will communicate with each other.
    This requires an understanding of the deployment scenarios your application must support. You must determine if all components will run within the same process, or if communication across physical or process boundaries must be supported— perhaps by implementing message-based interfaces.
  •  Keep crosscutting code abstracted from the application business logic as far as possible.
    Crosscutting code refers to code related to security, communications, or operational management such as logging and instrumentation. Mixing the code that implements these functions with the business logic can lead to a design that is difficult to extend and maintain. Changes to the crosscutting code require touching all of the business logic code that is mixed with the crosscutting code. Consider using frameworks and techniques (such as aspect oriented programming) that can help to manage crosscutting concerns.
  • Define a clear contract for components.Components, modules, and functions should define a contract or interface specification that describes their usage and behavior clearly. The contract should describe how other components can access the internal functionality of the component, module, or function; and the behavior of that functionality in terms of pre-conditions, post-conditions, side effects, exceptions, performance characteristics, and other factors.








    Saturday 28 May 2016

    Inversion of Control Principle

    https://msdn.microsoft.com/en-us/magazine/jj991965.aspx   MVVMLight

    This is where IOC containers come in handy. The term inversion of control means that the act of creating and keeping the instance is not the responsibility of the consuming class anymore, as it is in traditional object-oriented programming, but is instead delegated to an external container. While this is not an obligation, the cached instances are often injected into the consumer class’s constructor or made available through a property of the consumer. This is why we talk about dependency injection, or DI. some time called the "Hollywood Principle: Don't call us, we'll call you"

    The term is related to, but different from, the dependency inversion principle, which concerns itself with decoupling dependencies between high-level and low-level layers through shared abstractions.

     Note that dependency injection is not necessary for IOC containers to work, but it is a convenient way to decouple the consumer from the cached instances and from the cache itself.


    In object-oriented programming, there are several basic techniques to implement inversion of control. These are:
     
    Quite a few Dependency Injection IOC containers are available on the market.  for example Mvvmlights simpleIoc container.

    Monday 23 May 2016

    Tree Collection Code Example

    http://www.siepman.nl/blog/post/2013/07/30/tree-node-nodes-descendants-ancestors.aspx




     COLLECTION items must have a parent id property, null indicates a root


     var rootNodes = Node<Location>.CreateTree(locations, l => l.Id, l => l.ParentId);

                  
        var rootNode = rootNodes.Single(); // Assume that all nodes belong to one tree, otherwise there would be more rootnodes
        var theNetherlands = rootNode.Descendants.Single(n => n.Value.Name == "The Netherlands");
     
     


    //COPY AND PASTE THE BELOW CODE INTO VS AND DO A RESHARPER CODE CLEANUP TO GET RID ON THE SPACES


    public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>>



    {
    public Node<T> Parent { get; private set; }
    public T Value { get; set; }
    private readonly List<Node<T>> _children = new List<Node<T>>();

    public Node(T value)



    {

    Value = value;

    }
    public Node<T> this[int index]



    {
    get { return _children[index]; }



    }
    public Node<T> Add(T value, int index = -1)



    {
    var childNode = new Node<T>(value);



    Add(childNode, index);
    return childNode;



    }
    public void Add(Node<T> childNode, int index = -1)



    {
    if (index < -1)



    {
    throw new ArgumentException("The index can not be lower then -1");



    }
    if (index > Children.Count() - 1)



    {
    throw new ArgumentException(
    "The index ({0}) can not be higher then index of the last iten. Use the AddChild() method without an index to add at the end"


    .FormatInvariant(index));

    }
    if (!childNode.IsRoot)



    {
    throw new ArgumentException(
    "The child node with value [{0}] can not be added because it is not a root node.".FormatInvariant(



    childNode.Value));

    }
    if (Root == childNode)



    {
    throw new ArgumentException(
    "The child node with value [{0}] is the rootnode of the parent.".FormatInvariant(childNode.Value));



    }
    if (childNode.SelfAndDescendants.Any(n => this == n))



    {
    throw new ArgumentException(
    "The childnode with value [{0}] can not be added to itself or its descendants.".FormatInvariant(



    childNode.Value));

    }
    childNode.Parent = this;
    if (index == -1)



    {

    _children.Add(childNode);

    }
    else


    {

    _children.Insert(index, childNode);

    }

    }
    public Node<T> AddFirstChild(T value)



    {
    var childNode = new Node<T>(value);



    AddFirstChild(childNode);
    return childNode;



    }
    public void AddFirstChild(Node<T> childNode)



    {

    Add(childNode, 0);

    }
    public Node<T> AddFirstSibling(T value)



    {
    var childNode = new Node<T>(value);



    AddFirstSibling(childNode);
    return childNode;



    }
    public void AddFirstSibling(Node<T> childNode)



    {

    Parent.AddFirstChild(childNode);

    }
    public Node<T> AddLastSibling(T value)



    {
    var childNode = new Node<T>(value);



    AddLastSibling(childNode);
    return childNode;



    }
    public void AddLastSibling(Node<T> childNode)



    {

    Parent.Add(childNode);

    }
    public Node<T> AddParent(T value)



    {
    var newNode = new Node<T>(value);



    AddParent(newNode);
    return newNode;



    }
    public void AddParent(Node<T> parentNode)



    {
    if (!IsRoot)



    {
    throw new ArgumentException("This node [{0}] already has a parent".FormatInvariant(Value), "parentNode");



    }
    parentNode.Add(this);



    }
    public IEnumerable<Node<T>> Ancestors



    {
    get


    {
    if (IsRoot)



    {
    return Enumerable.Empty<Node<T>>();



    }
    return Parent.ToIEnumarable().Concat(Parent.Ancestors);



    }

    }
    public IEnumerable<Node<T>> Descendants



    {
    get { return SelfAndDescendants.Skip(1); }



    }
    public IEnumerable<Node<T>> Children



    {
    get { return _children; }



    }
    public IEnumerable<Node<T>> Siblings



    {
    get { return SelfAndSiblings.Where(Other); }



    }
    private bool Other(Node<T> node)



    {
    return !ReferenceEquals(node, this);



    }
    public IEnumerable<Node<T>> SelfAndChildren



    {
    get { return this.ToIEnumarable().Concat(Children); }



    }
    public IEnumerable<Node<T>> SelfAndAncestors



    {
    get { return this.ToIEnumarable().Concat(Ancestors); }



    }
    public IEnumerable<Node<T>> SelfAndDescendants



    {
    get { return this.ToIEnumarable().Concat(Children.SelectMany(c => c.SelfAndDescendants)); }



    }
    public IEnumerable<Node<T>> SelfAndSiblings



    {
    get


    {
    if (IsRoot)



    {
    return this.ToIEnumarable();



    }
    return Parent.Children;



    }

    }
    public IEnumerable<Node<T>> All



    {
    get { return Root.SelfAndDescendants; }



    }
    public IEnumerable<Node<T>> SameLevel



    {
    get { return SelfAndSameLevel.Where(Other); }



    }
    public int Level



    {
    get { return Ancestors.Count(); }



    }
    public IEnumerable<Node<T>> SelfAndSameLevel



    {
    get { return GetNodesAtLevel(Level); }



    }
    public IEnumerable<Node<T>> GetNodesAtLevel(int level)



    {
    return Root.GetNodesAtLevelInternal(level);



    }
    private IEnumerable<Node<T>> GetNodesAtLevelInternal(int level)



    {
    if (level == Level)



    {
    return this.ToIEnumarable();



    }
    return Children.SelectMany(c => c.GetNodesAtLevelInternal(level));



    }
    public Node<T> Root



    {
    get { return SelfAndAncestors.Last(); }



    }
    public void Disconnect()



    {
    if (IsRoot)



    {
    throw new InvalidOperationException(
    "The root node [{0}] can not get disconnected from a parent.".FormatInvariant(Value));



    }
    Parent._children.Remove(this);
    Parent = null;



    }
    public bool IsRoot



    {
    get { return Parent == null; }



    }
    IEnumerator<T> IEnumerable<T>.GetEnumerator()



    {
    return _children.Values().GetEnumerator();



    }
    IEnumerator IEnumerable.GetEnumerator()



    {
    return _children.GetEnumerator();



    }
    public IEnumerator<Node<T>> GetEnumerator()



    {
    return _children.GetEnumerator();



    }
    public override string ToString()



    {
    return Value.ToString();



    }
    public static IEnumerable<Node<T>> CreateTree<TId>(IEnumerable<T> values, Func<T, TId> idSelector,
    Func<T, TId?> parentIdSelector) where TId : struct


    {
    var valuesCache = values.ToList();
    if (!valuesCache.Any()) return Enumerable.Empty<Node<T>>();



    T itemWithIdAndParentIdIsTheSame =

    valuesCache.FirstOrDefault(v => IsSameId(idSelector(v), parentIdSelector(v)));
    if (itemWithIdAndParentIdIsTheSame != null)



    {
    throw new ArgumentException(
    "At least one value has the samen Id and parentId [{0}]".FormatInvariant(



    itemWithIdAndParentIdIsTheSame));

    }
    var nodes = valuesCache.Select(v => new Node<T>(v));
    return CreateTree(nodes, idSelector, parentIdSelector);



    }
    public static IEnumerable<Node<T>> CreateTree<TId>(IEnumerable<Node<T>> rootNodes, Func<T, TId> idSelector,
    Func<T, TId?> parentIdSelector) where TId : struct


    {
    var rootNodesCache = rootNodes.ToList();
    var duplicates = rootNodesCache.Duplicates(n => n).ToList();
    if (duplicates.Any())



    {
    throw new ArgumentException(
    "One or more values contains {0} duplicate keys. The first duplicate is: [{1}]".FormatInvariant(



    duplicates.Count, duplicates[0]));

    }
    foreach (var rootNode in rootNodesCache)



    {
    var parentId = parentIdSelector(rootNode.Value);
    var parent = rootNodesCache.FirstOrDefault(n => IsSameId(idSelector(n.Value), parentId));
    if (parent != null)



    {

    parent.Add(rootNode);

    }
    else if (parentId != null)



    {
    throw new ArgumentException(
    "A value has the parent ID [{0}] but no other nodes has this ID".FormatInvariant(parentId.Value));



    }

    }
    var result = rootNodesCache.Where(n => n.IsRoot);
    return result;



    }
    private static bool IsSameId<TId>(TId id, TId? parentId) where TId : struct


    {
    return parentId != null && id.Equals(parentId.Value);



    }
    public static bool operator ==(Node<T> value1, Node<T> value2)



    {
    if ((object)(value1) == null && (object)value2 == null)



    {
    return true;



    }
    return ReferenceEquals(value1, value2);



    }
    public static bool operator !=(Node<T> value1, Node<T> value2)



    {
    return !(value1 == value2);



    }
    public override bool Equals(Object anderePeriode)



    {
    var valueThisType = anderePeriode as Node<T>;
    return this == valueThisType;



    }
    public bool Equals(Node<T> value)



    {
    return this == value;



    }
    public bool Equals(Node<T> value1, Node<T> value2)



    {
    return value1 == value2;



    }
    bool IEqualityComparer.Equals(object value1, object value2)



    {
    var valueThisType1 = value1 as Node<T>;
    var valueThisType2 = value2 as Node<T>;
    return Equals(valueThisType1, valueThisType2);



    }
    public int GetHashCode(object obj)



    {
    return GetHashCode(obj as Node<T>);



    }
    public override int GetHashCode()



    {
    return GetHashCode(this);



    }
    public int GetHashCode(Node<T> value)



    {
    return base.GetHashCode();



    }

    }

     
    public static class NodeExtensions


    {
    public static IEnumerable<T> Values<T>(this IEnumerable<Node<T>> nodes)



    {
    return nodes.Select(n => n.Value);



    }

    }

     
    public static class OtherExtensions


    {
    public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source,
    Func<TSource, TKey> selector)



    {
    var grouped = source.GroupBy(selector);
    var moreThen1 = grouped.Where(i => i.IsMultiple());
    return moreThen1.SelectMany(i => i);



    }
    public static bool IsMultiple<T>(this IEnumerable<T> source)



    {
    var enumerator = source.GetEnumerator();
    return enumerator.MoveNext() && enumerator.MoveNext();



    }
    public static IEnumerable<T> ToIEnumarable<T>(this T item)



    {
    yield return item;



    }
    public static string FormatInvariant(this string text, params object[] parameters)



    {
    return string.Format(text, parameters);



    }

    }