How to Decouple Infrastructure Code from Business Logic in Java

  • Post last modified:December 15, 2022
  • Reading time:3 mins read

Overview

  • As a developer, when we write code to satisfy the business requirements, our code generally can be classified as infrastructure code and main business logic. In infrastructure code, we typically code about exception handling, tracing code, logging, bootstrap code, config initialization code, and so on.
  • These codes take our initial lines of code and make our function lengthier. After writing all the infrastructure-related code, we then write our main business logic. As a Code reader, I would always hate that since I have to read unnecessary boiler-plate code before even getting to the main logic. It unnecessarily increases code read time, and also makes it hard to reach to the main logic.
  • Hence, as a clean coder, I should always write a code that is decoupled. Our infrastructure code should always be taken care of as common code and it should not be bothered inside the main business logic.

Understanding By Example

  • In our feature1 class, we should always see code related to business logic, and any infrastructure-related code should be abstracted from the business logic class and moved to the common class.
  • The best way to achieve such decoupling in java 8 and beyond is using function as a parameter.
  • Let’s say we need to map the input customer object to the user object. So in the main Feature 1 class, I should only write the logic for mapping, and no other boiler-plate code should be present in this class.
  • I will write mapping logic in a separate CustomerMapper class and there I will handle all the null checks or format checks etc. Then I should create a lambda function that takes input as a customer and returns the user as output.
import java.util.function.Function;

public class Feature1 {

    // write logic in main mapper class
    static Function<Customer,User> function = c-> CustomerMapper.mapToUser(c);

    public static void main(String[] args){
        Customer customer = new Customer();
        User user = new User();
        GenericMapper.to(customer, function); // Pass function as parameter
    }


}
  • I should also create a generic mapper that takes Type T as the input parameter and Type U as the output parameter.
    and all the infrastructure-related code such as exception handling, logging, tracing, etc. should be written inside this generic mapper.
  • Now I can pass any function that takes input T and expect output U to this generic mapper function.
import java.util.function.Function;

public class GenericMapper {

    public static <T,U> U to(T input, Function<T,U> logic){
        U u = null;

        // write infrastructure code here
        try {
           u = logic.apply(input);
            return u;
        }catch(Exception ex){
            // exception handling
        }
        return u;
    }
}
  • Now if there is a requirement for Feature2, I only need to write business logic and not the infrastructure code since it’s already taken care of in GenericMapper class.

Conclusion

  • In this article, we discussed how we can use lambda functions to separate infrastructure code and business logic code and make our code cleaner by being nice to our code reader.

Bonus Tip

Leave a Reply