3 wise men on Tell Don't ask

A story on Tell Don't ask Principle

wisemen.jpg

John, Doe, and Marcus are three good friends. They have over 20 years of experience Java/JEE stack and working in IBM, Cognizant and TCS respectively. They have immense experience in design pattern and all the new technologies and respected by their colleague for Exceptional insight on the Technology Stack.
They are planning to go for a vacation in the upcoming weekend to enjoy their spare time with lots of Burger, whiskey, and cooking. John has an Outhouse in a village so they planned to go there by driving.
At last, the day came they pack their Beer, Whiskey, and Burger and headed towards John outhouse it was far away from Town. At evening they reached the outhouse and prepare some snacks for their whiskey and sit together in a round table to enjoy Chicken roast and Whiskey.
Suddenly the power cut happens, The room is so dark that no one can see anything, from outside they can hear the Call of Cricket, Marcus put on his mobile flashlight, now they can see each other.
Doe breaking the silence by saying,
“Oh well, this ambiance is perfect for a horror story can anyone share any real life experience?”
Marcus replied in a witty way
“Umm No, I believe all we are from town and busy with IT industry so sorry can not share any horror experience but can share some Java experience which still frightened me”
John and Doe’s Architect instinct flashed with this proposal.
They said, “ Oh yes what would be more good to discuss about something which frightened us an Architect in this ambiance, it is same as Horror Story :).”

Marcus slowly demonstrated the problem.
Marcus: As an Architect when I design a solution for a problem it always frightened me what we encapsulate and what portion we expose to our client program?
John and Doe Nodded their head.
Marcas Continue with his speech,
There are lots of OOPs principle which says how judiciously you can encapsulate your classes or API from outside world.
Take an Example, The  Tell Don’t Ask principle, It says us always tell to Object, in a layman term instruct Object what to do never query for an internal state and take a decision based on that because then you loose control over the object.
Take a simple example suppose I want to write a Parcel Delivery Service and there are two domain Objects Parcel and Customer so how should we design it.
If I write following code fragments

/**
*
*/
package com.example.basic;

/**
* @author Shamik Mitra
*
*/
public class PercelDeliveryService {
   
   
    public void deliverPercel(Long customerId){
       
        Customer cust = customerDao.findById(customerId);
        List<Percel> percelList = percelDao.findByCustomerId(customerId);
       
        for(Percel percel : percelList){
           
            System.out.println("Delivering percel to " + cust.getCustomerAddress());
            //do all the stuff for delivery
        }
       
    }

}


According to Tell Don’t Ask it is a violation and should be avoided. In Parcel Delivery Service I try to fetch or ask Customer Address so I can perform the delivery operation so here I query the internal state of the Customer.
And why it is dangerous?
If later if the delivery functionality change says now it also include an email address or mobile so I have to expose these details so exposing more and more internal state think about the other services they may also use the same email address or Customer address. Now If I want to change the Customer Address return type String to Address Object then I need to change all services where it has been used, so a gigantic task to perform and increases risk factor of breaking functionality. Another point is as internal state is exposed to many services there is always a risk to pollute the internal state by a service and it is hard to detect which service changed the state. In one word I loose the control over my object as I don’t know which services use/modify my Object internal state. Now if my object is used by the Internal services of a monolith application I can search the usage in IDE and refactor them but If the Object is exposed through an API and This API used by other organization then I am gone, It really hurts our company reputation and as an architect, I would be fired.
So I can say

Action: More you Expose Internal state
Outcome:  Increase coupling, Increases Risk, Increase Rigidity.


Now again look the solution I provided,
Doe chuckled and guess which point Marcus trying to make so he interrupted him and start saying.
Doe: So Marcus you want to tell if we follow Tell Don’t Ask principle then there are couple of ways we can refactor the problem
1. make an association between Customer and Parcel . and it would be lazy loaded and deliver method should be in Customer Object so from the service we call deliver then deliver method fetch parcel list and deliver it, so if the Internal return type change from String to Address only “deliver” method should be affected.
Like this,
/*
*
*/
package com.example.basic;

/**
* @author Shamik Mitra
*
*/
public class ParcelDeliveryService {
   
   
    public void deliverParcel(Long customerId){
       
        Customer cust = customerDao.findById(customerId);
        cust.deliver();
    }

}

public class Customer{

    public void deliver(){
        List<Percel> percelList = getPercelList();
      for(Percel percel : percelList){
           
            System.out.println("Delivering percel to " + this.getCustomerAddress());
            //do all the stuff for delivery
        }
       
    }
}



By doing this I maintain Tell Don’t Ask principle properly and decreases the risk of exposing internal state and free to modify my class attributes as all behaviors are tied locally with attributes a nice way to achieve encapsulation.
2. We can create a command Object where we pass the Parcel details and pass the command object to Customer Model, the delivery method extracts the Parcel list and deliver it to the respective customer.
But both policy breaks another principle that is Single Responsibility Principle, SRP(Single Responsibility Principle) says A class has only one reason to change.
But If I think in a reverse way, why we write Services? Because each service does one job like Person Delivery Service responsible for “delivery parcel related “ operations so it maintains SRP and this service only change if there are any changes in Parcel Delivery mechanism and if it breaks other services will not be affected unless other services depend on it.
But according to Tell Don’t Ask all Customer related behaviors should be moved into Customer class so we can tell /Instruct/command Customer class to do a task. So Now Customer class has much responsibility because all Customer-related service code now goes into Customer Model. So Customer has more reason to change so increase the risk factor of failing.
So Now we are back in the same problem risk factor.
If exposing internal state then the risk for modifying attribute if move all behavior into a class then the risk of modifying functionality break the system.
So SRP and Tell Don’t Ask principle contradict in this context.

John: John nodded his head and started with his husky voice, Yes this is really a problem
not only this, If we want to implement a cache in a service or want to implement Aggregation function like Percell delivery rate charge according to distance or Account type, Find most parcels sent to a locality we use Aggregator service where we ask for internal state and compute the result. So often we break Tell Don’t Ask principle. Even as per current trend, Model Object should be lightweight and should not be coupled with each other. If the business needs an information which distributes over multiple models we can write an aggregator service and query each model and compute the result , so we querying internal state. Think about Spring data.
Now If we look in another perspective, according to the Domain-driven Design, In a Parcel Delivery Context (Bounded context) is Customer responsible for delivering the parcel?
Absolutely not, In that context Delivery Boy is responsible for delivering the parcel to the customer. For that Delivery boy needs Parcel and Customer Model, and in that context only Customer name and Address details required and for parcel Id, parcel name will be required so as per DDD we create an Aggregate Model DeliveryBoy where we have two slick Model Customer and Percell because in this context we don’t need customer other details and parcel other details like customer account details, Customer birthdate etc, Context wise model is changed so no one big model for customer where all attribute and behaviour resides rather small slick models based on bounded context and an Aggregate model querying these slick model and perform the work.
By doing this we can mix and match SRP and Tell Don’t ask. For a service perspective we only tell /command DeliveryBoy Aggregate model to do something, so Service maintains SRP and Tell don’t ask, Our Aggregate model also maintain SRP but querying Customer and Parcel Model to do the operation.

Like



/**
*
*/
package com.example.basic;

/**
* @author Shamik Mitra
*
*/
public class ParcelDeliveryService {
   
   
    public void deliverParcel(Long customerId){
       
        DeliveryBoy boy = new DeliveryBoy();
        boy.deliver(customerId);
    }

}

public class DeliveryBoy{
    Customer cust;// Context driven model
    Percel percel;
    public void deliver(Long id){
        //do stuff
//load customer slick model
//load Percel slick model
//Deliver the same by quering

        }
       
    }
}

Marcus joined and says let's take one step further as DDD insists  Microservice, so in Microservice we try to break a monolith using functional decomposition( A function is a Bounded context in DDD) so one service doing one task maintains SRP principle and if we needed and information which distributes over multiple services we create an Aggregator service and querying individual service and do the task so Microservice often breaks Tell Don’t Ask
Doe joined and says So there is no silver bullet and not all principles are good in all context, Based on the context you have to judge which principles you follow sometimes you need to compromise, may for a specific principle viewpoint your code is bad but for a given context it is optimum.

Principles are generic and they are context free but real life solution are based on context so fit principles based on context not the reverse.

In the meantime Light comes, so John said here we are for fun let stop the discussion and concentrate on Whiskey and Roast !!!!
Everyone agreed and change the topic.
Conclusion : As a Narrator, My question to all viewers, what do you think about the talk they did, is there are any points they left off which needs attention while designing?

MicroServices with Devops Example



MicroServices with Devops  Example






In this post I show you How to create Micro service application and Manage it through Dev-ops.

To understand MicroServices please look at my blog post 

To understand Devops please look at my blog post


Tools are Used to implement Microservice
1.       Eclipse as Editor
2.       Java as Language
3.       Using REST Service to communicate between Microservices
4.       Maven as build tool
5.       Tomcat as Web server.


Tool are Used to Manage Microservices using Devops
1.       Jenkins as CI (Continous integration server).
2.       GitHub as SCM(Source control Management).


My intent is creating two Micro services


1.       An Order Service

2.       A Billing service

If client order for a product, Order Service project will communicate with Billing service project by REST API. Billing Service returns Product price if exists in the Product Catalog Otherwise It return a Message Product not found.

Order service accept this response and generate a new response to Client.


To Manage these Microservice, I using Devops

So that Two can be deployed independently, and Two projects are hosted in
GitHub. If someone do a change the code and push it in Github a build has been triggered by Jenkins and Once build is completed, Jenkins install the artifact that is the war file in Local Repo (maven Local REPO) then deploy it to Tomcat Server.

No Manual intervention is needed.


I have made it very simple but in real time there will be much more complexity than two simple services and we need Monitoring tool for health check for different projects which is absent in this example.

I have used Java for Two services but real time one could be in java another in PHP or Node JS any language.


Installation:

Java:  Install java from
Set JAVA_HOME in environment variables to java installation path.


Download Tomcat 6 Zip Version and extract it in your local drive that will your Tomcat installation directory.
Maven:
Extract the zip in your local file Set M2_Home as maven Installation path in your local directory.


Go to Git Command prompt after installation
Change Directory to C: Using Cd ../../
Now apply command (Need Internet connection)

after completing

again hit

Import two project in to Eclipse by import from local option.

If you want to create your new repositories, please go to Jenkins setup section and follow the step.

I prefer you create your new repositories to work independently. Unless if you clone it you will be dependent on me. If I gave you permission, then only you can push change on my project.







Jenkins :
 http://mirrors.jenkins-ci.org/war-stable/  (Take stable release. I use 1.65). Download war and deploy it to <Tomcat_installation_path>/webapps. In tomcat console when a message shows
“Jenkins full up and running” go to browser hit


First thing you need to require add plugins in to your local Jenkins Server

So Follow the steps
Go to Manage Jenkins from left panel
Go to manage Plugin
In Available Tab Search  with “git” and check gitplugin and github plugin
Hit install without restart button
Wait until installation finished.


Go to configure system from manage plugin
 






Create an account in GitHub and then add your username and your email Id.


Then Go to new Item create a Maven project.

Create a Job Name as “BuildandDeploy MicroOrderService”

Please see the following Screen shot




 
In the Source code management section set your Git repository URL
If you download source code from Git by download Zip and create your new repository and push the code

Steps are
After Download project from

and
using “ CloneorDownload”  button on GitHub
Extract Zip files

Create a project on Github using “+” button (Create new)
You will get a url like https://github.com/shami83/<projectname>.git


Execute following commands for two projects in your local computer

Go to  Git cmd
Go to the homefolder of the Extract project
a.       git init
b.      git add .
c.       git commit -m “demo”
d.      git push origin master

Please do the steps for two projects that will create two repositories in Git (require internet connection).

In jenkins on repository url put the url


If you clone it then you need my permission to push code to my stream, please send me a mail the at
mitrashamik@gmail.com or add a comment in blog.


Then put your credentials in credential section your username and password of Git repository.


Follow next step



In Build triggers section Check ” BuildWhen a change pushed to Git hub”

And poll SCM

Put value as * * * * * in schedule text-area

This will detect if any changes pushed on GitHub repository. If so this Jenkins target will run

Now Go to Build section
Set Root POM as pom.xml as you can see a workspace folder create under your Jenkins target where Jenkins pull the codebase from Git server and build it locally.

Set Goals and Option as clean install.
Install step is requires as it maintains your artifacts in your local maven repository.


Last I want to deploy it on Tomcat server

To do that follow the following steps

First go to Tomcat installation directory under conf folder
Open tomcat-users.xml in edit mode
Add following lines under <tomcat-users> tag.

<role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="tomcat" roles="admin,manager-script,manager-gui"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>


Then go to Jenkins job configure section

 





In War/EAR files put target\MicroOrderService.war as maven generates artifacts in target directory.


After that Set user name as tomcat in Tomcat6.x section password is tomcat
Tomcat Url is http://localhost:8080

Click save button that will create MicroOrderService job.


To check hit BuildNow button and go to console output, you will see
Jenkins pull code from GitHub then build it using maven install the artifacts then deploy it in your Tomcat Server



Perform Same thing for Billingservice, create a new Job and configure that.


Now to Check just make a change in Source code
Push it to Git using Git Cmd
Steps
git add .
git commit -m ”change”
git push origin master

go to Jenkins dashboard you will see a build is initiated and deployed.






Main Classes in Micro Order Source Code


OrderService.java


package com.microservice.order;
 import java.io.IOException;
import java.io.StringReader;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.microservice.model.BillingStatus;
import com.microservice.model.OrderStatus;

@Path("/order")
public class OrderService {

    @GET
    @Path("{name}")
    @Produces(MediaType.TEXT_XML)
    public OrderStatus getOrderStatus(@PathParam("name") String name)
            throws ClientProtocolException, IOException {
        System.out.println("Orerd Item name is ==>" + name);
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet getRequest = new HttpGet(
                "http://localhost:8080/billingservice/billingservice/billing/"
                        + name);

        String response = getResponse(httpClient, getRequest);
        BillingStatus stat = new BillingStatus();
        stat = this.converStringToObject(stat, response);
        System.out.println("Return Object from JAXB "  + stat);

        OrderStatus status = new OrderStatus();
        status.setOrderName(stat.getItem());
        status.setOrderCost(stat.getPrice());
        return status;
    }

    public String getResponse(DefaultHttpClient client, HttpGet request) {

        HttpResponse response = null;
        String apiOutput = null;

        try {

            response = client.execute(request);

            // verify the valid error code first
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new RuntimeException("Failed with HTTP error code : "
                        + statusCode);
            }

            HttpEntity httpEntity = response.getEntity();
            apiOutput = EntityUtils.toString(httpEntity);
           
           

            System.out.println(apiOutput);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return apiOutput;

    }

    public <T> T converStringToObject(T t, String res) {

        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(t.getClass());
            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

            t = (T) jaxbUnmarshaller.unmarshal(new StringReader(res));
        } catch (JAXBException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return t;

    }

}




Main Class in Billing service Module


package com.microservice.billing.service;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.microservice.billing.model.BillStatus;
import com.microservice.billing.utility.PriceCatalog;



@Path("/billing")
public class BillingService {
   
   
    @GET
    @Path("{name}")
    @Produces(MediaType.TEXT_XML)
    public BillStatus getOrderStatus(@PathParam("name")String name)
    {
       
        System.out.println("Check Item in Inventory ==>" + name);
        return PriceCatalog.getCatalog().getStatus(name.toLowerCase());
    }

}


PriceCatalog.java

package com.microservice.billing.utility;

import java.util.HashMap;   
import java.util.Map;

import com.microservice.billing.model.BillStatus;

public class PriceCatalog {
   
    private static PriceCatalog calalog=new PriceCatalog();
    private Map<String,String> priceMap=new HashMap<String,String>();
    private PriceCatalog()
    {
        init();
    }
   
    private void init()
    {
        priceMap.put("pen", "10");
        priceMap.put("shirt", "1000");
        priceMap.put("televison", "20000");
        priceMap.put("ac", "30000");
        priceMap.put("smartphone", "14000");
        priceMap.put("radio", "2000");
        priceMap.put("musicbox", "20000");
        priceMap.put("bike", "66000");
        priceMap.put("scooter", "26000");
        priceMap.put("pant", "100");
        priceMap.put("harddisc", "3000");
        // end of map
       
       
    }
   
    public static PriceCatalog getCatalog()
    {
        return calalog;
    }
   
    public BillStatus getStatus(String key)
    {
        BillStatus status= new BillStatus();
        if(key == null || priceMap.get(key)==null)
        {
            status.setItem("Sorry" + key + " Not found in Inventory!!");
            status.setPrice("Not Applicable");
            return status;
        }
        status.setItem(key.toUpperCase());
        String price =  priceMap.get(key) + " Rupees only.";
        status.setPrice(price);
        return status;
    }
   
   
   

}
Please Dowload Code base from


https://github.com/shami83/MicroOrderService
and 
https://github.com/shami83/BillingService