Separating Your Concerns- Antidote to Smart UI Part 5

So in the previous blog we have created IDiscountStrategy and applied it to  NullDiscountStrategy and TradeDiscountStrategy Classes.

We have only created Discount Pattern here… and we need to apply this on Price Object and driving factor regarding which Discount Pattern to be applied will be decided by Customer Type.So in order to apply Discount pattern on Price object lets create DiscountFactory class which does this job.

 public static class DiscountFactory
    {
        public static IDiscountStrategy GetDiscountStrategyFor(CustomerType customerType)
        {
            switch (customerType)
            {
                case CustomerType.Trade:
                    return new TradeDiscountStrategy();
                default:
                    return new NullDiscountStrategy();
            }

        }

    }

With discount Strategies in place .. now lets implement Price Object…

 public class Price
    {
        private IDiscountStrategy _discountStrategy = new NullDiscountStrategy();
        private decimal _rrp;
        private decimal _sellingPrice;
        public Price(decimal sellingPrice,decimal rrp)
        {
            _rrp = rrp;
            _sellingPrice = sellingPrice;
        }
        public void SetDiscountStrategyTo(IDiscountStrategy discountStrategy)
        {
            _discountStrategy = discountStrategy;
        }
        public decimal SellingPrice
        {
            get { return _discountStrategy.ApplyExternalDiscountsTo(_sellingPrice); }
        }
        public decimal RRP
        {
            get { return _rrp; }
        }
        public decimal Discount
        {
            get
            {
                if(RRP>SellingPrice)
                {
                    return RRP - SellingPrice;
                }
                else
                {
                    return 0;
                }
            }
        }
        public decimal Savings
        {
            get
            {
                if (RRP > SellingPrice)
                    return (1 - SellingPrice / RRP);
                else
                    return 0;
            }
        }
    }

Now we need to apply this discount Pattern to Products List which we will do thru an Extension method

    public static class ProductListExtensionMethod
    {
        public static void Apply(this IList<Product>;products,
            IDiscountStrategy discountStrategy)
        {
            products.ToList().ForEach
                (p =&gt; p.Price.SetDiscountStrategyTo(discountStrategy));

        }
    }

Domain Service

with Domain in place.. we need to have a service in domain so that client(typically service layer ) can communicate with Domain layer.. lets create a Domain Service(name it Product Service)

public  class ProcuctService
    {
        private IProductRepository _productRepository;
        public ProcuctService(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
        public IList<Product? GetAllProductsFor(CustomerType customerType)
        {
            IDiscountStrategy discountStrategy =
                DiscountFactory.GetDiscountStrategyFor(customerType);

            IList<Product> products = _productRepository.FindAll();
            products.Apply(discountStrategy);

            return products;
        }


    }

Now Domain Service layer is in player.. lts implement Service layer which would communicate with Domain Service layer


public class ProductService
    {
        private model.ProcuctService _productService;

        public ProductService(model.ProcuctService productService)
        {
            _productService = productService;
        } 

        public ProductListResponse GetAllProductsFor(
            ProductListRequests productListRequest)
        {
            ProductListResponse productListResponce =
                new ProductListResponse();
            try
            {
                IList<model.Product> productEntities =
                   _productService.GetAllProductsFor(productListRequest.CustomerType);

                productListResponce.Products =
                    productEntities.ConvertToProductListViewModel();

                productListResponce.Success = true;
               
            }
            catch (Exception ex)
            {
                productListResponce.Success = false;
                productListResponce.Message = ex.Message.ToString();

                throw;
            }
            return productListResponce;
        }
    }

Advertisements

Separating Your Concerns- Antidote to Smart UI Part 4

Now lets start building Service layer

ServiceLayer

  • The role of Service layer is to act as entry point in to application..sometimes it is known as a FACADE
  • Service layer provides the Presentation Layer with a strongly Typed View model, sometimes called Presentation Model
  • Add a new class to ASPPatterns.Chap3.Layered.Service project named ProductViewModel
  • 
    public class ProductViewModel
        {
            public int ProductId { get; set; }
            public string Name { get; set; }
            public string  RRP { get; set; }
            public string SellingPrice { get; set; }
            public String Discount { get; set; }
            public string Savings { get; set; }
    
        }
    
  • Client will be interacting with Service layer and for this we will use REQUEST-RESPONSE Messaging Pattern
  • From client we will be getting a Request to Service and Request will be of CustomerType. So create a Request based on CustomerType
  •  public class ProductListRequestcs
        {
            public CustomerType CustomerType { get; set; }
        }
    
  • Response from Service would be of ProductViewModel along with Success/Failure message..
     public class ProductListResponse
        {
            public bool Success { get; set; }
            public string Message { get; set; }
            public IList<ProductViewModel> Products { get; set; }
    
        }
    
  • If you observe in Response we are sending ProductViewModel as Response.. so we need to have a mechanism to convert Products in to ProductViewModels.. so lets build this mechanism..
  • Below class is an extension method to Products, which will Transform Products to ProductViewModels
  •   public static IList<ProductViewModel> ConvertToProductListViewModel(this IList<Model.Product> products)
            {
                IList<ProductViewModel> productViewModels = new List<ProductViewModel>();
    
                foreach (var product in products)
                {
                    productViewModels.Add(product.ConvertToProductViewModel());
                }
    
                return productViewModels;
            }
    
  • The below class is an extension method to Product , which will convert Product to ProductViewModel
  •  public static ProductViewModel ConvertToProductViewModel(this Model.Product product)
            {
                ProductViewModel productViewModel = new ProductViewModel();
    
                productViewModel.ProductId = product.Id;
                productViewModel.Name = product.Name;
                productViewModel.RRP = string.Format("{0:C}", product.Price.RRP);
                productViewModel.SellingPrice = string.Format("{0:C}", product.Price.SellingPrice);
    
                if(product.Price.Discount > 0)
                {
                    productViewModel.Discount = string.Format("{0:C}", product.Price.Discount);
                }
    
                if (product.Price.Savings > 0 && product.Price.Savings<1)
                {
                    productViewModel.Savings = product.Price.Savings.ToString("#%");
                }
                return productViewModel;
            }
    

    Now if you remember we have yet to build our domain… lets start Building Domain Domain

    Domain Layer

    Gear Up!!!!!… we are going to implement some Patterns here…

    See the below diagram…

  • We have Product Class and Product Class contains price class which will implement Discount Strategy
  • What is the need of Strategy Pattern in our implementation..??
    Strategy Pattern enables algorithms to be selected and changed at runtime and there is a need for our Price object to display Discount based on Customer Type. so we are going to apply Discount Algorithms to Price Object.

    Lets implement Strategy Pattern Discount Strategy

  • Create an Interface IDiscountStrategy in ASPPatterns.Chap3.Layered.Model
  • public interface IDiscountStrategy
        {
            decimal ApplyExternalDiscountsTo(decimal originalSalesPrice);
    
        }
    
  • Now we have created Interface IDiscountStrategy , lets add two implementations of Discount Strategy TradeDiscountStrategy and NullDiscountStrategy
  • Trade discount strategy will apply discount to original sales price..see below implementation
  • 
    public class TradeDiscountStrategy : IDiscountStrategy
        {
            public decimal ApplyExternalDiscountsTo(decimal originalSalesPrice)
            {
                decimal price = originalSalesPrice;
    
                price = price * .095M;
                return price;
    
            }
        }
    
    
  • If you observe below implementation of NullDiscountStrategy, we are employing Null Object Pattern here
  •     public class NullDiscountStrategy : IDiscountStrategy
        {
            public decimal ApplyExternalDiscountsTo(decimal originalSalesPrice)
            {
                return originalSalesPrice;
            }
        }
    

Separating Your Concerns- Antidote to Smart UI Part 3

Now.. if you remember our use case.. we have products.. and price of the products will vary based on Type of Customer..

So to proceed further , let us create two objects Product and Customer(since customer will be a type..it should be an enum in our Domain model “ASPPatterns.Chap3.Layered.Model”.

Product.cs


public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Price Price { get; set; }
    }

Since Price of the product has a behaviour now..it is going to vary by the Customer Type.. it should be an object.. so for now create an empty price class as shown below..we will see implementation of Price class later(why..?? since we are going to apply new patterns in this class..)

 public class Price
    {
    }

Now create CustomerType enum

 public enum CustomerType
    {
        Standard=0,
        Trade=1
    }

lets retro where we are heading…

Typical Three Layered Architecture

This is what we are going to do…

  • Presentation Layer : we are half way done here..if you remember in previous blog we have created our presentation layer
  • Business Logic Layer : we have only created basic infrastructure i.e we have created Product Class, defined price class and Customer class
  • Data Access Later Yet to start

    Note If you observe the above diagram we will be having two Service classes
    1. First one In Domain model..
    2. One in the service layer..
    Reason for this…??? since we are following Layered architecture… Responsibility of Service class in domain layer will be fetching data from Repository Later and Responsibility of Service class in Service Layer will be interacting with service class in Domian Layer and Giving Presentation Model to Presentation Layer..

    Lets code to get clarity…

    Now we will build repository layer/Data Access layer…

  • As with the Smart UI, you need a database to store the products. Create a database in the WebUI project with the same schema, name, and data that you used in the Smart UI Blog
  • Creating database

    We will be using Linq to SQL here…(this is the first time i am working with LinqtoSQL.. but it rocks…!! will try to post more about LinqToSQL after finishing this book)

  • Add a new Linq to SQL class to the ASPPatterns.Chap3.Layered.Repository project
    by right-clicking the project name and selecting Add ➪ New Item. Then select Linq to SQL Classes,and name the class Shop.dbml
  • With the Server Explorer window open, drag the Products table onto the design surface. Visual Studio creates a Linq to SQL entity named Product
  • Now What.. what is the use of Linq to SQL.. you will see now..

  • Create Interface IProductRepository in ASPPatterns.Chap3.Layered.Model Project..Why a Repository Interface in Model ..?? Domain Service Layer will interact with Data Layer to retrieve products. and we don’t want Model Project to be concerned with what kind of technologies will be used to query the data
  • public interface IProductRepository
        {
            IList<Product> FindAll();
        }
    
  • Now create Concrete Implementation of IProductRepository in ASPPatterns.Chap3.Layered.Repository project, by creating class ProductRepository
  •  public IList<Model.Product> FindAll()
            {
                var products = from p in new ShopDataContext().Products
                               select new Model.Product
                               {
                                   Id = p.ProductId,
                                   Name = p.ProductName,
                                   //Price = new Model.Price(p.RRP, p.SellingPrice) -- Price implmentation
                               };
    
                return products.ToList();
    
            }
    
  • Check out line 3 in the above code… since we have implemented LinqToSQl we can directly query over Product table… isn’t it cool
  • coming to line 8 in the above code.. we need to implement Price model..which we will do later and come back here again..
  • With this we have completed implementation of Data Layer … in the next blog we will implement Service layer

    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…

    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