Architecture - Understanding SOLID Design Principles

OOPS Principles (SOLID Principles):

Understanding SOLID Architecture Principles / Design / Styles

SOLID is an acronym for the first five object-oriented design (OOD) principles. 

It makes programmer life easy - used to write maintainable and extendible code.
Best suited for agile development.
 


Architecture - Key Design Principles

 

 

 

 

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.

    • For example, ASP.NET MVC Framework (Model, View, Controller)

  • Single Responsibility principle.

    • Each component or module should be responsible for only a specific feature or functionality, or aggregation of cohesive functionality.

    • For example, ASP.NET MVC Framework (Model, View, Controller)

  • Principle of Least Knowledge (a.k.a. Law of Demeter or LoD).

    • A component or object should not know about internal details of other components or objects.

    • For example, In ASP.NET MVC Framework view is not aware of what Model or Controller are doing.

  • 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.

    • For example, In ASP.NET MVC Helper classes are used so that HTML code repetition can be removed.

  • 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").

In general, Minimize the complexity by separating the design into different areas of concern.

For example,  the user interface (UI), business processing, and data access all represent different areas of concern.

Within each area, the components you design should focus on that specific area and should not mix code from other areas of concern.

For example, UI processing components should not include code that directly accesses a data source, but instead should use either business components or data access components to retrieve data.


Architecture - Exploring Dependency Injection (DI)

What is Dependency Injection (DI)?

  • Dependency Injection (DI) is all about removing dependencies from your code.
  • DI is a pattern used for decoupling components and layers in the system.
  • Dependency injection means passing a dependent object as a parameter to a method/class, rather than having the method/class create the dependent object.
  • Dependency injection provides opportunities to -
    • simplify code,
    • abstract dependencies between objects,
    • and automatically generate dependent object instances.

How to achieve Dependency Injection (DI)?

This can be done in two step:

  1. Identify dependency

  2. Inject dependency when needed

    • Do not instantiate the dependencies explicitly in the class. Instead, declaratively express dependencies in the class definition. 
    • These dependencies are usually provided as interfaces for further decoupling and to support testability.
    • The pattern seeks to establish a level of abstraction via a public interface and to remove dependencies on components by supplying 'plug-in' architecture.
      • I.e. individual components are tied together by the architecture rather than being linked together themselves. 
    • Use a Builder object (Factory pattern) to obtain valid instances of the object's dependencies and pass them to the object during the object's creation and/or initialization. 
    • Dependencies can be injected using a constructor, property setter, an interface injection-
      • Constructor injection:

        • IoC pattern is implemented through injecting dependencies into a component when it is constructed, where the interface parameter which is referenced internally inside the class is been exposed through the constructor.
      • Setter injection:

        • It is enabling the injection of the interface implementation through the public property with the set block. 
      • Components of Dependency Injection: 

        • dependent consumer.
          • The dependent object describes what software component it depends on to do its work. 
        • Interface contracts.
          • Declaration of the component's dependencies.
        • An injector (aka a provider or container
          • Creates instances of classes that implement a given dependency interface on request.
          • The injector decides what concrete classes satisfy the requirements of the dependent object, and provides them to the dependent.

Explain Dependency Injection (DI) with Example?

Let's say we have ‘PDFCreator’ Class, which will create PDF for review as shown:


public class PDFCreator
{
 private Review review
 public PDFCreator()
 {
 review= new Review();
 }
}

Identify dependency-

In above code, we have created dependency with Review object. What if we have two different kinds of reviews? Or any other change in Review object that we don't want to expose to PDF Creator.

Inject dependency-

In order to remove this dependency, we would like to remove dependency and inject it whoever needed. To do this we modify the code as shown below.


public class PDFCreator
{
 private IReview iReview
 public PDFCreator(IReview iReview)
 {
 this.iReview = iReview;
 }
}

In this fix, we are now referring to Interface for ‘Review’ class and injecting it when we create ‘PDFCreator’ object, and because of this interface, we can inject different Review objects if any and removing the dependency of ‘Review’ class makes it ready for changes if any.

 

Pros of Dependency Injection (DI):

  • Useful for test-driven development - without DI it can be difficult to test modular system because the components under test are highly coupled to the rest of the system.
  • Best for developing a modular system whose components can be replaced without requiring recompilation.
  • Provides the ability to swap dependency implementations in different environments.
  • Provides a mechanism for sharing resources throughout an application.
  • Inversion of control is a practical way to reduce code duplication, and if you find yourself copying an entire method and only changing a small piece of the code

Cons of Dependency Injection (DI):

  • Use DI only when required - Do not stick it everywhere. If you will overuse this pattern, you will make very complicated design and even more complicated code.
  • Process may have a minor impact on performance

Examples of Tools / Libraries Used for Dependency Injection (DI):

  • Castle Windsor,
  • Unity

How Dependency Injection (DI) helps in Unit Testing?

It's very easy to write unit tests for your code because it depends on nothing else than the objects it accepts in its constructor/setters and you can easily initialize them with the right objects in isolation.


Architecture - Evaluating Inversion of Control (IoC)

Background:

  • In traditional programming, the flow of the business logic is determined by objects that are statically assigned to one another. 
  • With Inversion of Control principle, the flow depends on the object graph that is instantiated by the assembler and is made possible by object interactions being defined through abstractions. 

What is Inversion of Control (IoC)?

  • Inversion of Control (IoC) is an object-oriented programming principle
  • where the object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis.
  • Inversion of control is all about removing dependencies from your code.
  • IoC is a pattern used for decoupling components and layers in the system.

IoC Implementation Ways:

  • Using Dependency Injection (a constructor, property setter, an interface injection)
  • Using a contextualized lookup
  • Using a factory pattern
  • Using a service locator pattern
  • Using Unity Framework (Constructor, Property, Interface, Service Locator Injections)
  • Using Prism Framework (Unity Framework + MEF + Other modular programming features)
  • Using Spring.Net, Google Guice (Complicated implementations)

How to achieve Inversion of Control (IoC) using Dependency Injection (DI)?

This can be done in two step:

  1. Identify dependency

  2. Inject dependency when needed

    • Do not instantiate the dependencies explicitly in the class. Instead, declaratively express dependencies in the class definition. 
    • These dependencies are usually provided as interfaces for further decoupling and to support testability.
    • The pattern seeks to establish a level of abstraction via a public interface and to remove dependencies on components by supplying 'plug-in' architecture.
      • I.e. individual components are tied together by the architecture rather than being linked together themselves. 
    • Use a Builder object (Factory pattern) to obtain valid instances of the object's dependencies and pass them to the object during the object's creation and/or initialization. 
    • Dependencies can be injected using a constructor, property setter, an interface injection-
      • Constructor injection:

        • IoC pattern is implemented through injecting dependencies into a component when it is constructed, where the interface parameter which is referenced internally inside the class is been exposed through the constructor.
      • Setter injection:

        • It is enabling the injection of the interface implementation through the public property with the set block. 
      • Components of Dependency Injection: 

        • A dependent consumer.
          • The dependent object describes what software component it depends on to do its work. 
        • Interface contracts.
          • Declaration of the component's dependencies.
        • An injector (aka a provider or container
          • Creates instances of classes that implement a given dependency interface on request.
          • The injector decides what concrete classes satisfy the requirements of the dependent object, and provides them to the dependent.

Explain Inversion of Control (IoC) with an example?

Let's say we have ‘PDFCreator’ Class, which will create PDF for review as shown:


public class PDFCreator
{
 private Review review
 public PDFCreator()
 {
 review= new Review();
 }
}

Identify dependency-

In above code, we have created dependency with Review object. What is we have two different kinds of reviews? Or any other change in Review object that we don't want to expose to PDF Creator.

Inject dependency-

In order to remove this dependency, we would like to remove dependency and inject it whoever needed. To do this we modify the code, as shown below.


public class PDFCreator
{
 private IReview iReview
 public PDFCreator(IReview iReview)
 {
 this.iReview = iReview;
 }
}

In this fix, we are now referring to Interface for ‘Review’ class and injecting it when we create ‘PDFCreator’ object, and because of this interface, we can inject different Review objects if any and removing the dependency of ‘Review’ class makes it ready for changes if any.

 

Pros of Inversion of Control (IoC):

  • Useful for test-driven development - without IoC, it can be difficult to test because the components under test are highly coupled to the rest of the system.

  • Best for developing a modular system whose components can be replaced without requiring recompilation.

  • Provides the ability to swap dependency implementations in different environments.

  • Provides a mechanism for sharing resources throughout an application.

  • Inversion of control is a practical way to reduce code duplication, and if you find yourself copying an entire method and only changing a small piece of the code

Cons of Inversion of Control (IoC):

  • Use IoC only when required - Do not stick it everywhere. If you will overuse this pattern, you will make very complicated design and even more complicated code.

Examples of Tools / Libraries Used for Inversion of Control (IoC)? :

  • Castle Windsor,

  • Unity

How Inversion of Control (IoC) helps in Unit Testing?

It's very easy to write unit tests for your code because it depends on nothing else than the objects it accepts in its constructor/setters and you can easily initialize them with the right objects in isolation.


Architecture - Styles and Patterns Categories

Architecture Patterns and Styles Categories

 

 

Architecture Patterns / Styles Categories:

  • Communication
    • Service-Oriented Architecture (SOA)
    • Message Bus
  • Deployment
    • Client/Server
    • 3-Tier
    • N-Tier
  • Domain
    • Domain Driven Design
  • Structure
    • Component-Based
    • Object-Oriented
    • Layered Architecture

Communication

○ Service-Oriented Architecture (SOA)

■ Refers to applications that expose and consume functionality as a service using contracts and messages.

○ Message Bus

■ An architecture style that prescribes use of a software system that can receive and send messages using one or more communication channels, so that applications can interact without needing to know specific details about each other.

Deployment

○ Client/Server

■ Segregates the system into two applications, where the client makes requests to the server. In many cases, the server is a database with application logic represented as stored procedures.

○ 3-Tier / N-Tier

■ Segregates functionality into separate segments in much the same way as the layered style, but with each segment being a tier located on a physically separate computer.

Domain

○ Domain Driven Design

■ An object-oriented architectural style focused on modeling a business domain and defining business objects based on entities within the business domain.

Structure

○ Component-Based

■ Decomposes application design into reusable functional or logical components that expose well-defined communication interfaces.

○ Object-Oriented

■ A design paradigm based on division of responsibilities for an application or system into individual reusable and self-sufficient objects, each containing the data and the behavior relevant to the object.

○ Layered Architecture

■ Partitions the concerns of the application into stacked groups (layers).  


Architecture - Enterprise Solution Design Patterns

Enterprise Solution Patterns Using Microsoft .NET

https://i-msdn.sec.s-msft.com/dynimg/IC154851.gif

 

Figure shows levels of abstraction vs view points

  • levels of abstraction: architecture, design, and implementation.
  • viewpoints (or lenses into the solution): database, application, deployment, and infrastructure perspectives.

Architecture - Application Design Patterns

A design pattern is an approach or solution to a commonly occurring programming problem.