Separating Your Concerns- Antidote to Smart UI Part 2

..Honestly to be mentioned.. i have completed the third chapter..and there are lot new stuff which i am unaware of it …it took me for a while to understand some concepts.. i really hope.. i will get my self a clear understanding after completing this Separating Your Concerns- Antidote to Smart UI series…

in the Book.. first Domain model has been presented and then parts like web services,infrastructure, repo model were built on Domain model.. finally developing Presentation layer/User Experience Layer..

But In this series i will first start with Presentation Layer…as i said i want to be clear about what i have done in this series..instead of just putting what is in the book

The below diagram represents the architecture flow which we are going to develop..

before going in to Presentation layer.. lets build our database as we have done in AntiPattern

  • Create a database in the WebUI project with the same schema, name, and data that you used in the Smart UI exercise
  • User Experience Layer

  • Open the default.aspx source view and edit the HTML markup so it matches what follows:
  • <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
    
       
    <asp:DropDownList AutoPostBack="true" ID="ddlCustomerType" runat="server">
    <asp:ListItem Value="0">Standard</asp:ListItem>
    <asp:ListItem Value="1">Trade</asp:ListItem>
    </asp:DropDownList>
    <asp:Label ID="lblErrorMessage" runat="server" ></asp:Label>
    <asp:Repeater ID="rptProducts" runat="server" >
    <HeaderTemplate>
    <table>
    <tr>
    <td>Name</td>
    <td>RRP</td>
    <td>Selling Price</td>
    <td>Discount</td>
    <td>Savings</td>
    </tr>
    <tr>
    <td colspan="5"><hr /></td>
    </tr>
    </HeaderTemplate>
    <ItemTemplate>
    <tr>
    <td><%# Eval("Name") %></td>
    <td><%# Eval("RRP")%></td>
    <td><%# Eval("SellingPrice") %></td>
    <td><%# Eval("Discount") %></td>
    <td><%# Eval("Savings") %></td>
    </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
    </asp:Repeater>
    
    </asp:Content>
    
    

    if you compare the code with Smart UI AntiPattern, this is super clean.. there are no database bound controls in UI.. there are no delete/update/select database bounded controls here…

    For now don’t worry about code behind file..i will revisit this section later again in the series..

    Separating Your Concerns- Antidote to Smart UI

    Separating Your Concerns

    An antidote to smart UI antipattern is the notion of Layering your applications.

    Layering an application is a form of separation of concerns and can be achieved via namespaces,folders or with Separate Projects . Below Figure shows Typical
    ASP.NET enterprise level Layerd Architecture

    Let us rewrite the code which we have written in SmartUI Antipattern.. aligning to Seperation of Concerns…which we have discussed above…

    Refactoring to the principles.. lets start coding…!!

    • Create a new blank solution in Visual Studio and name it ASPPatterns.Chap3.Layered
    • Add a new class library project to the solution by right-clicking the solution name and selecting Add ➪ New Project. Name the new project ASPPatterns.Chap3.Layered.Repository .
    • Add a further three class library projects to the solution named:
    • 1. ASPPatterns.Chap3.Layered.Model
      2. ASPPatterns.Chap3.Layered.Service
      3. ASPPatterns.Chap3.Layered.Presentation

    • Add a new web application to the project by selecting Add ➪ New Project and selecting the Web Application Project. Name the project ASPPatterns.Chap3.Layered.WebUI.
    • Right-click on the ASPPatterns.Chap3.Layered.Repository project and add a project reference to the ASPPatterns.Chap3.Layered.Model project
    • Right-click on the ASPPatterns.Chap3.Layered.Service project and add a project reference to the ASPPatterns.Chap3.Layered.Repository and ASPPatterns.Chap3.Layered
      .Model projects
    • Right-click on the ASPPatterns.Chap3.Layered.Presentation project and add a project reference to the ASPPatterns.Chap3.Layered.Model and ASPPatterns.Chap3.Layered
      .Service projects
    • Right-click on the ASPPatterns.Chap3.Layered.WebUI web application and add a project reference to the ASPPatterns.Chap3.Layered.Model, ASPPatterns.Chap3.Layered.Service,ASPPatterns.Chap3.Layered.Presentation, and ASPPatterns.Chap3.Layered.Repository projects
    • This is how solution looks like…

    in the Smart UI Application, we have written Business logic in the Presentation layer itself..

    Now we will separate this..All domain related stuff goes in to ASPPatterns.Chap3.Layered.Model ..

    will start coding in next blog.. tune in folks .. lots and lots of new stuff is going to come…

    Now that we have defined Use Case to add new Business rule… its time to apply in code

  • we are adding new control here.. a combobox which will decide if trade discout has to be applicable.. so i have added new control.. and made relevant changes
  • 
    <body>
        <form id="form1" runat="server">
        <div>
        <asp:DropDownList ID="ddlDiscountType" runat="server" AutoPostBack="true"
                           OnSelectedIndexChanged="ddlDiscountType_SelectedIndexChanged">
            <asp:ListItem Value="0">No Discount</asp:ListItem>
            <asp:ListItem Value="1">Trade Discount</asp:ListItem>
        </asp:DropDownList>
                             
            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
                DataKeyNames="ProductId"
                DataSourceID="SqlDataSource1"
                EmptyDataText="There are no data records to display." 
                OnRowDataBound="GridView1_RowDataBound">
                <Columns>
                    <asp:BoundField DataField="ProductId" HeaderText="ProductId" ReadOnly="True" SortExpression="ProductId" />
                    <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
                    <asp:BoundField DataField="RRP" HeaderText="RRP" SortExpression="RRP" />
                    <asp:TemplateField HeaderText="SellingPrice">
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblSellingPrice"
                                Text='<%#Bind("SellingPrise")%>'>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="Discount">
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblDiscount"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="Savings">
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblSavings"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ShopConnectionString1 %>" DeleteCommand="DELETE FROM [Products] WHERE [ProductId] = @ProductId" InsertCommand="INSERT INTO [Products] ([ProductId], [ProductName], [RRP], [SellingPrise]) VALUES (@ProductId, @ProductName, @RRP, @SellingPrise)" ProviderName="<%$ ConnectionStrings:ShopConnectionString1.ProviderName %>" SelectCommand="SELECT [ProductId], [ProductName], [RRP], [SellingPrise] FROM [Products]" UpdateCommand="UPDATE [Products] SET [ProductName] = @ProductName, [RRP] = @RRP, [SellingPrise] = @SellingPrise WHERE [ProductId] = @ProductId">
                <DeleteParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                </DeleteParameters>
                <InsertParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                </InsertParameters>
                <UpdateParameters>
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                      <asp:Parameter Name="ProductId" Type="Int32" />
                </UpdateParameters>
            </asp:SqlDataSource>
        
        </div>
        </form>
    </body>
    

    and this is the code behind file..

    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
            {
                if(e.Row.RowType==DataControlRowType.DataRow)
                {
                    decimal RRP = decimal.Parse(((System.Data.DataRowView)(e.Row.DataItem))["RRP"].ToString());
                    decimal SellingPrice = decimal.Parse(((System.Data.DataRowView)(e.Row.DataItem))["SellingPrise"].ToString());
    
                    Label lblSavings = (Label)e.Row.FindControl("lblSavings");
                    Label lblDiscount = (Label)e.Row.FindControl("lblDiscount");
                    Label lblSellingPrice = (Label)e.Row.FindControl("lblSellingPrice");
    
    
                    lblSavings.Text = displaySavings(RRP, ApplyExtraDiscountTo(SellingPrice));
                    lblDiscount.Text = displayDiscount(RRP,  ApplyExtraDiscountTo(SellingPrice));
    
                   
    
                }
            }
    
    private decimal ApplyExtraDiscountTo(decimal originalSalesPrice)
             {
                decimal price = originalSalesPrice;
    
                int discountType = Int16.Parse(this.ddlDiscountType.SelectedValue);
    
                if(discountType==1)
                {
                    price = price * 0.95M;
                }
                return price;
            }
    
     private string displayDiscount(decimal rRP, decimal sellingPrice)
             {
                string discoutText = "";
                if(rRP>sellingPrice)
                {
                    discoutText = string.Format("{0:c}", (rRP - sellingPrice));
                }
                return discoutText;
            }
    
     private string displaySavings(decimal rRP, decimal sellingPrice)
            {
                string savingsText = "";
    
                if(rRP>sellingPrice)
                {
                    savingsText = (1 - (sellingPrice / rRP)).ToString("#%");
                }
                return savingsText;
            }
    
     protected void ddlDiscountType_SelectedIndexChanged(object sender, EventArgs e)
            {
                GridView1.DataBind();
            }
    
    

    and after the code modifications.. this is how now our page looks like..

    with out discount

    with discount

    Bad coding.. Bad Coding .. Bad Coding..!!!!!!

    Whats wrong with the way we have implemented..???

    Well nothing ,if the application stops evolving here..but if its part of larger application , the capibility of applying Discounts(remember Trade discounts..which we have applied..) will be needed else where..and in its present state.. logic is embedded in single page. this means we have to duplicate our code when new features are added….

    In the next blog will discuss about some new concepts to tackle these kind of situations….

    Anti Pattern – Smart UI Part 3

    Now that we have added two columns.. now its time to add code behind code…

    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
            {
                if(e.Row.RowType==DataControlRowType.DataRow)
                {
                    decimal RRP = decimal.Parse(((System.Data.DataRowView)(e.Row.DataItem))["RRP"].ToString());
                    decimal SellingPrice = decimal.Parse(((System.Data.DataRowView)(e.Row.DataItem))["SellingPrise"].ToString());
    
                    Label lblSavings = (Label)e.Row.FindControl("lblSavings");
                    Label lblDiscount = (Label)e.Row.FindControl("lblDiscount");
    
                    lblSavings.Text = displaySavings(RRP,SellingPrice);
                    lblDiscount.Text = displayDiscount(RRP, SellingPrice);
    
                }
            }
    
            private string displayDiscount(decimal rRP, decimal sellingPrice)
             {
                string discoutText = "";
                if(rRP>sellingPrice)
                {
                    discoutText = string.Format("{0:c}", (rRP - sellingPrice));
                }
                return discoutText;
            }
    
            private string displaySavings(decimal rRP, decimal sellingPrice)
            {
                string savingsText = "";
    
                if(rRP>sellingPrice)
                {
                    savingsText = (1 - (sellingPrice / rRP)).ToString("#%");
                }
                return savingsText;
            }
    

    Add the above three methods..to display savings and discount..if you run our code this is how it looks like

  • what have done ..??
  • The GridView1_RowDataBound method is called when each data row is bound to data in the
    GridView control. The method obtains the RRP and selling price and uses DisplayDiscount and DisplaySavings methods to work out the correct discount. Then it updates the corresponding label server controls.

    Broken things

    • Violating Single Responsibility Principle – The page is not only taking the responsibility of the business logic(we are caliculating savings and discount here) and also responsible for the data access requirements
    • Its hard to test.. the way its implemented now…

    Now that we have put business logic in the code behind file… lets add some more business logic to it..

  • This is the use case- trade discount to be applied to the prices so that they reflect an extra 5 percent of savings( we are going to apply trade discount via drop down).
  • Anti Pattern – Smart UI Part 2

    Lets start building SmartUI application

    • Fire up Visual Studio and create a new blank solution named ASPPatterns.Chap3.SmartUI
      and add a new web application to it named ASPPatterns.Chap3.SmartUI.Web
    • add a new SQL express database to
      the project by right-clicking on the web site and selecting Add ➪ New Item and selecting a SQL Server Database. Name the database Shop.mdf.
    • Now you need to add a table Products to the database
    • Add the data to the table
    • With the table created, you can simply drag and drop it onto the Default.aspx page.This automatically creates a GridView and adds a SQLDataSource control to the page.You should now be able to run the web application and see all the products listed
      from the database

    This is how now code looks like

    <body>
        <form id="form1" runat="server">
        <div>
        
            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductId" DataSourceID="SqlDataSource1" EmptyDataText="There are no data records to display.">
                <Columns>
                    <asp:BoundField DataField="ProductId" HeaderText="ProductId" ReadOnly="True" SortExpression="ProductId" />
                    <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
                    <asp:BoundField DataField="RRP" HeaderText="RRP" SortExpression="RRP" />
                    <asp:BoundField DataField="SellingPrise" HeaderText="SellingPrise" SortExpression="SellingPrise" />
                </Columns>
            </asp:GridView>
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ShopConnectionString1 %>" DeleteCommand="DELETE FROM [Products] WHERE [ProductId] = @ProductId" InsertCommand="INSERT INTO [Products] ([ProductId], [ProductName], [RRP], [SellingPrise]) VALUES (@ProductId, @ProductName, @RRP, @SellingPrise)" ProviderName="<%$ ConnectionStrings:ShopConnectionString1.ProviderName %>" SelectCommand="SELECT [ProductId], [ProductName], [RRP], [SellingPrise] FROM [Products]" UpdateCommand="UPDATE [Products] SET [ProductName] = @ProductName, [RRP] = @RRP, [SellingPrise] = @SellingPrise WHERE [ProductId] = @ProductId">
                <DeleteParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                </DeleteParameters>
                <InsertParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                </InsertParameters>
                <UpdateParameters>
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                      <asp:Parameter Name="ProductId" Type="Int32" />
                </UpdateParameters>
            </asp:SqlDataSource>
        
        </div>
        </form>
    </body>
    

    This is how our page looks like..

  • Now add extra columns to the grid to show discount and savings. we will add these two columns as template columns.and also include OnRowDataBound event
  • This now the modified code after adding template columns..

    <body>
        <form id="form1" runat="server">
        <div>
        
            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
                DataKeyNames="ProductId"
                DataSourceID="SqlDataSource1"
                EmptyDataText="There are no data records to display." 
                OnRowDataBound="GridView1_RowDataBound">
                <Columns>
                    <asp:BoundField DataField="ProductId" HeaderText="ProductId" ReadOnly="True" SortExpression="ProductId" />
                    <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
                    <asp:BoundField DataField="RRP" HeaderText="RRP" SortExpression="RRP" />
                    <asp:BoundField DataField="SellingPrise" HeaderText="SellingPrise" SortExpression="SellingPrise" />
                    <asp:TemplateField HeaderText="Discount">
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblDiscount"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="Savings">
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblSavings"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ShopConnectionString1 %>" DeleteCommand="DELETE FROM [Products] WHERE [ProductId] = @ProductId" InsertCommand="INSERT INTO [Products] ([ProductId], [ProductName], [RRP], [SellingPrise]) VALUES (@ProductId, @ProductName, @RRP, @SellingPrise)" ProviderName="<%$ ConnectionStrings:ShopConnectionString1.ProviderName %>" SelectCommand="SELECT [ProductId], [ProductName], [RRP], [SellingPrise] FROM [Products]" UpdateCommand="UPDATE [Products] SET [ProductName] = @ProductName, [RRP] = @RRP, [SellingPrise] = @SellingPrise WHERE [ProductId] = @ProductId">
                <DeleteParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                </DeleteParameters>
                <InsertParameters>
                    <asp:Parameter Name="ProductId" Type="Int32" />
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                </InsertParameters>
                <UpdateParameters>
                    <asp:Parameter Name="ProductName" Type="String" />
                    <asp:Parameter Name="RRP" Type="Decimal" />
                    <asp:Parameter Name="SellingPrise" Type="Decimal" />
                      <asp:Parameter Name="ProductId" Type="Int32" />
                </UpdateParameters>
            </asp:SqlDataSource>
        
        </div>
        </form>
    </body>
    

    Anti Pattern – Smart UI Part 1

    what we are going to build in Anti Pattern – Smart UI series ..?

    In these series , i will be creating an example of SmartUI web application and will start to add business logic to it..and then we should see how the concerns and responsibilities are intertwined, and then we we Rewrite our code adhering to Separation of Concerns principle.

    What are we going to build..?
    I will build a page in ASP.NET Web forms,that will display list of Products sale, displaying their name, recommended retail price (RRP), selling price, discount, and savingspercentage.
    Let’s start building the application

    Starting Chapter 3 layering Your application and separating Your Concerns

    hey there…

    i have completed chapter 2 with yesterdays’s blog… now i am starting chapter 3 of the book..

    Whats in this chapter..?

    • Benefits of Layered design over traditional ASP.NET web forms code behind model..
    • The concepts of Logic Layering and the seperation of your Application Concerns
    • The responsibility of each distinct layer in enterprise level ASP.NET application
    • Example code refactoring from Smart UI antipattern to a layered approach .

    so in this chapter we are going to build a SmartUI Application.. and see short coming of it.. so that we will discuss and refactor the SmartUI Application to layered application.. in the next blog will be building the Application.. its coding timee.. 🙂

    A QUICK PATTERN EXAMPLE- Refactoring to Principles – Continuation Part 5

    The Final Problem
    HTTP context is tightly coupled with Product Service class. suppose consider in future, HTTP Context has been changed to MemCached.. we need to modify our product Service class!! How can we avoid this…??

    Adapter Pattern

    yes!!! by employing Adapter Pattern , we can be able to resolve this pattern

    Adapter Pattern : Basically Converts the interface of a class into another interface clients expect… let us see the implementation now…!!

    First let us abstract HTTP Context from Product Service class (By implementing Interface).

    Create an interface ICacheStorage as shown below

    public interface ICacheStorage
        {
            void Remove(string key);
            void Add(string key,object data);
            T Retreive<T>(string key);
        }
    

    Implement this interface in Product Service class

    commented code is before implemenation

     public class ProductService
        {
            private IProductRepository _productRepository;
            private ICacheStorage _cacheStorage;
    
            //public ProductService(IProductRepository productRepository )
            //{
            //    _productRepository = productRepository;
            //}
    
            public ProductService(IProductRepository productRepository,ICacheStorage cacheStorage)
            {
                _productRepository = productRepository;
                _cacheStorage = cacheStorage;
            }
    
    
            public IList<Product> GetAllProductsIn(int categoryId)
            {
                IList<Product> products;
                string storageKey = string.Format("Products_in_category_id_{0}", categoryId);
    
    
               
                //products = (List<Product>)HttpContext.Current.Cache.Get(storageKey);
                products = _cacheStorage.Retreive<List<Product>>(storageKey);
                
                if (products == null)
                {
                    products = _productRepository.GetAllProductsIn(categoryId);
    
                   // HttpContext.Current.Cache.Insert(storageKey,products);
                    _cacheStorage.Add(storageKey,products);
                }
                return products;
    
            }
    
        }
    

    Now Abstraction completed.. i.e we have removed dependency of HTTPContext from Product Service class.. 🙂

    Now we need to make HTTPContext to implement our ICacheStorage interface… can it implement directly.. nope.. so we need to have a way so that HTTPContext will implement our ICacheStorage.. that is where Adapter Pattern comes in to Picture

    Adapter Pattern UML diagrams
    Adapter Pattern UML

    Adapter Pattern UML

    Adapter Pattern UML for our use case

    Adapter Pattern UML for our Use Case

    let us now implement….

    create a class HttpContextCacheAdapter which is a wrapper for HTTP Context cache.

    
     public class HttpContextCacheAdapter: ICacheStorage
        {
            public void Remove(string key)
            {
                HttpContext.Current.Cache.Remove(key);
            }
    
            public void Add(string key, object data)
            {
                HttpContext.Current.Cache.Insert(key, data);
            }
    
            public T Retreive<T>(string key)
            {
                T itemStored =(T) HttpContext.Current.Cache.Get(key);
                if (itemStored == null)
                    itemStored = default(T);
                return itemStored;
            }
        }
    
    

    What have we achieved by implementing this Adapter pattern…??

    let us suppose if we need to implement another cache service like MemCached or velocity,all you would need to do is create an Adapter that allows
    the ProductService class to interact with the caching storage provider via the common ICacheStorage
    interface

    finally our code is clean no refactoring for now !!!….at least i can see that… 🙂

    A QUICK PATTERN EXAMPLE- Refactoring to Principles – Continuation Part 4

    In the Previous blog we have seen the problem after refactoring the code. will discuss more about the problem in this blog

    Problem

    the ProductService class is still responsible for creating the concrete
    implementation and currently it is impossible to test the code without a valid ProductRepository
    class.

    Solution

    Dependency Injection can help us here.

    Before Implementing Dependency injection

    Product Service class is tied to concrete implementation of Product repository class because, its responsibility of Product Service class to create instance of Product Repository Class. see the current implementation below

     
      public class ProductService
      {
          public ProductService()
          {
              _productRepository = new ProductRepository();
          }
      }
    
    

    After Implementing Dependency injection

    Dependency injection can move the responsibility of creating the product repository implementation out of Product Service class.and having it injected via class constructor.
    See the implementation below

     public class ProductService
        {
            private IProductRepository _productRepository;
    
           //dependency injection,injecting via constructor
            public ProductService(IProductRepository productRepository )
            {
                _productRepository = productRepository;
            }
           //implementation    
         
        }
    

    So what have we achieved by implementing Dependency Injection over here ..????

    • This enables a substitute to be passed product service class during testing, which enables you to test Product Service class to test in Isolation
    • ensuring that product Service class adheres to Single Responsibility Principle i.e it is now concerned with only retreiving data from cache or repository and not for creating concrete repository IProductRepository implemenation

    The last Problem one more…!! ahh yes.. there is one more problem in our implementation. which is HTTP Cache dependency in Product Service class. which we need to look upon… will discuss this problem in next blog 🙂

    A QUICK PATTERN EXAMPLE- Refactoring to Principles – Continuation Part 3

    Resolving the coding Horror!!!

    1. Problem

    The first problem which we are going to discuss here is dependency of Product Service class with Product Repository class i.e Product Service class is fragile

    Solution

    To resolve this issue of dependency, we can employ the Dependency Inversion Principle to decouple Product Service class from Product Repository, by having both depend on an Abstraction –an Interface

    Let us code now!!!

    1. create an Interface from ProductRepositoryClass, name it IProductRepository

    public interface IProductRepository
        {
            IList<Product> GetAllProductsIn(int CategoryId);
        }
    

    2.Implement the interface in ProductRepository class

    public class ProductRepository : IProductRepository
        {
            public IList<Product> GetAllProductsIn(int CategoryId)
            {
                IList<Product> products = new List<Product>();
    
                //Database operation to populate products
                return products;
            }
        }
    

    3.Update Product Service Class, make it refer to Product Repository Interface rather than ProductRepository class

    
      public class ProductService
        {
            // Product repository
            private IProductRepository _productRepository;
    
            public ProductService()
            {
                _productRepository = new ProductRepository();
            }
    
            public IList<Product> GetAllProductsIn(int categoryId)
            {
               // implementation
    
            }
    
        }
     

    What have we achieved by doing this

    • Product Service class now depends on an abstraction rather than concrete implementation , this means that the product service class is completely ignorant of any implementation, ensuring that it is less fragile and code base as a whole is less resilient to change
    • however there is one problem with this implementation . The Product Service class is still responsible for creating concrete implementation and currently it is impossible to test the code with out valid ProductRepository Class.

    Will discuss more about the problem in next blog 🙂