The Business logic layer: Active Record Pattern

Active Record Pattern is popular pattern that is especially effective when the underlying data base model matches the domain model. i.e Typically a business object exists for each and every table in the database

  • The business object represents a single row in table and contains data and behaviour
  • In Active Record Pattern each business object is responsible for its own persistence and business logic
  • Active Record Pattern is great for simple applications that have one to one mapping between data model and Business model
  • It is also good to use if you are building applications with “data first” approach
  • In the .NET world, one of the most popular open source Active Record frameworks is
    the Castle ActiveRecord project that is built upon NHibernate

Implementing Active Record Pattern
A Typical example for Active Record Pattern will be Blogging Application having posts ad comments.. below is the class representation

Create the following two tables..

  • Create a new Solution named ASPPatterns.Chap4.ActiveRecord
  • Add a new C# class library to the solution named ASPPatterns.Chap4.ActiveRecord.Model
  • Add new MVC web application named ASPPatterns.Chap4.Active Record.UI.MVC.
  • Add references to castleproject to the solution
  • Add the following two model classes Comment and Posts in ASPPatterns.Chap4.ActiveRecord.Model

[ActiveRecord("Comments")]
    public class Comment :ActiveRecordBase<Comment>
    {
        [PrimaryKey]
        public int Id { get; set; }
        [Property]
        public string  Text { get; set; }
        [Property]
        public string Author { get; set; }
        [Property]
        public DateTime DateAdded { get; set; }
        [BelongsTo("PostId")]
        public Post Post { get; set; }

    }

 [ActiveRecord("Posts")]
    public class Post :ActiveRecordBase<Post>
    {
        [PrimaryKey]
        public int Id { get; set; }
        [Property]
        public string Text { get; set; }
        [Property]
        public DateTime DateAdded { get; set; }

        public string ShortText
        {
            get
            {
                if(Text.Length>20)
                {
                    return Text.Substring(0, 20) + "......";
                }
                return Text;
            }
        }
        [HasMany]
        public IList<Comment> Comments { get; set; }

        public static Post FindLatestPost()
        {
            SimpleQuery<Post> post =
                new SimpleQuery<Post>(@"from post p
                          order by p.DateAdded desc");

            return (Post) post.Execute()[0];
        }
    }

The Business logic layer: Transaction Script pattern

There are Typically 4 Popular/known Patterns to design/build the domain.. which are

  • Transaction Script Pattern
  • Active Record Pattern
  • Anemic Model Pattern
  • Domain Model Pattern

Not all applications are equal, and require complex architecture to encapsulate Business logic of a system..As a dev..its important to understand the strengths and weakness of all the domain logic patterns..

In this blog… we shall discuss on Transaction ScriptPattern

Transaction Script Pattern

  • Of the four domain logic patterns, Transaction Script Patterns is easiest to Understand and get up running with
  • Transaction Script Patterns follows a Procedural style of development rather than Object Oriented Approach..
  • How Transaction Pattern is implemented..?
    Typically A single static class corresponding to your business transaction will be created and all the workflow(business rules,validation etc) which is required to complete the business transaction will be pertained in that class.

    When Transaction Script Patterns should be implemented..?
    Transaction Script Pattern is great for Small Applications, with little or no logic and mostly not likely to grow like a monster in future..

    Example source code for a Typical Transaction Script Pattern

    public static class HolidayService
        {
            public static bool BookHolidayFor(int employeeId,DateTime from,DateTime to)
            {
                bool booked = false;
                TimeSpan numberOfDaysRequestedForHolidays = to - from;
    
                if(numberOfDaysRequestedForHolidays.Days>0)
                {
                    int holidaysAvailabile = GetHolidaysRemainingFor(employeeId);
    
                    if(holidaysAvailabile >= numberOfDaysRequestedForHolidays.Days)
                    {
                        SubmitHolidayBookingFor(employeeId, from, to);
                    }
    
                }
    
    
    
            }
    
            private static void SubmitHolidayBookingFor(int employeeId, object from, object to)
            {
                // implementation
            }
    
            private static int GetHolidaysRemainingFor(int employeeId)
            {
               // implementation
            }
    
            public static List<EmployeeDTO> GetAllEmployeesOnLeaveBetween(
    DateTime From, DateTime To)
            {
                //  implementation
            }
    
            public static List<EmployeeDTO> GetAllEmployeesWithHolidayRemaining()
            {
                //  implementation
            }
    
            
        }
        public class EmployeeDTO
        {
    
    
        }
    

    As you can see , the entire Business Logic is encapsulated in methods..example,here BookHolidayFor is responsible for data retrieval,validations,Business logic to determine if Holiday can be taken..

    This type of Procedural Programming is fine if you are dealing with small application and business logic is not going to expand further drastically..

    But if you know that your application will grow, then you might want to rethink if you are going for this pattern…its is recommended to go for more scalable pattern like Active Record Pattern.. which i will discuss in the next blog..

    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&gt;SellingPrice)
                    {
                        return RRP - SellingPrice;
                    }
                    else
                    {
                        return 0;
                    }
                }
            }
            public decimal Savings
            {
                get
                {
                    if (RRP &gt; 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;
            }
        }