Deciphering the Mysteries of Domain, Business, and Application Logic

Adnan Khan
Bazaar Engineering
Published in
6 min readJun 2, 2024

Introduction

Hi there! I’m a seasoned software engineer with over a decade of experience in the ever-evolving world of coding. If you’re like me, you might have spent countless hours in your early career in tech meetings or during code reviews wondering what your senior colleagues meant when they tossed around terms like business logic, domain logic, and application logic. Maybe you watched talks by Uncle Bob, Eric Evans, and Martin Fowler where these terms popped up frequently. And if you’re still puzzled, don’t worry — you’re not alone!

Here is one example copied from Uncle Bob post Screaming Architecture

Your business objects should be plain old objects that have no dependencies on frameworks or databases or other complications. Your use case objects should coordinate your business objects. And all of them together should be testable in-situ, without any of the complications of frameworks. (copied from Uncle Bob post Screaming Architecture)

So in my early career, these terms were like cryptic jargon used by industry leaders like Uncle Bob, Martin Fowler, Eric Evans, and others in their talks. My senior engineers would use them to impress juniors, product teams, and business teams alike. During code reviews, pair programming sessions, and tech meetings, these words were thrown around so much that they started sounding like code-speak gibberish. Fast forward to now, and as a senior engineer myself, I still find myself scratching my head at times, especially when developing Android applications.

But worry not! Let’s break down these terms with some humor and practical examples so you can finally understand where to put which logic. And yes, we’ll look at some “what not to do” examples to keep things interesting!

I use the term ‘the logic of logic’ because it requires a considerable amount of brain energy and logic to put them in the right place.

The Logic of Logic: A Brief Overview

  • Domain Logic: The rules and operations specific to your application’s domain. Think of it as the heart of your business rules.
  • Business Logic: A subset of domain logic, but more focused on policies and workflows that drive your business operations.
  • Application Logic: The glue that holds everything together — the part that handles how users interact with the system.

Misplaced Domain Logic: Android Example

Now, let’s see how domain logic can go astray:

public class Product {
private String name;
private double price;
private int quantity;

public Product(String name, double price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}

// Domain logic should be here, but let's mess it up
public double calculateTotalPrice() {
return price * quantity;
}
}

public class ShoppingCartActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cart);

// Misplaced domain logic - oh no!
Product product = new Product(50.0, 2);
double total = product.price * product.quantity; // Recalculating in Activity
displayTotal(total);
}

private void displayTotal(double total) {
TextView totalView = findViewById(R.id.total);
totalView.setText(String.format("$%.2f", total));
}
}

Here, we should have used the calculateTotalPrice method in the Product class, but instead, we reimplemented the calculation in ShoppingCartActivity.

You might be thinking, “This guy is still writing code in activities; he’s a dinosaur.” But for the sake of this example, I wrote it in an activity, so don’t label me a dinosaur — just focus on the example. 🦖

An Android Example Gone Wrong: Misplaced Business Logic

Imagine you’re working on an e-commerce app. Here’s what you should not do with business logic:

public class CheckoutActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_checkout);

// Business logic in the Activity - yikes!
double total = GetCartTotalAmount();
if (isGoldCustomer()) {
total = total * 0.9; // Apply 10% discount for gold customers
}
displayTotal(total);
}

private double GetCartTotalAmount() {
// Calculate total from cart items
return 100.0; // Simplified for illustration
}

private boolean isGoldCustomer() {
// Determine if the customer is a gold customer
return true; // Simplified for illustration
}

private void displayTotal(double total) {
// Display the total amount in the UI
TextView totalView = findViewById(R.id.total);
totalView.setText(String.format("$%.2f", total));
}
}

Here, we’ve placed business logic (Gold discount ) directly in the CheckoutActivity. This is not ideal because Activities should focus on UI and user interaction, not business rules.

So the domain logic resides in domain models like the Product Model above but the business logic is more about the policies like Gold customers, Special discount, etc

Misleading Application Logic: Android Example

Sometimes, the application logic can also end up in the wrong place, leading to a tangled mess. Here’s an example:

ppublic class CheckoutRepository {

private List<Product> products = new ArrayList<>();
private MutableLiveData<String> errorMessage = new MutableLiveData<>();

public void addProduct(Product product) {
products.add(product);
}

public double calculateTotal() {
double sum = 0;
for (Product product : products) {
sum += product.getPrice() * product.getQuantity();
}
return sum;
}


// Application logic wrongly placed here
public boolean shouldNavigateToConfirmation(TextView errorView) {
double total = calculateTotal();
if (total > 0) {
return true;
} else {
errorView.setText("Cart is empty!"); // Direct UI update in repository
return false;
}
}

public MutableLiveData<String> getErrorMessage() {
return errorMessage;
}

public void checkCartStatus() {
double total = calculateTotal();
if (total == 0) {
errorMessage.setValue("Cart is empty!"); // Trigger UI update
}
}
}

Here, the repository layer is incorrectly making a decision about whether to navigate to the confirmation screen based on the total. Also, the repository directly updates a TextView with an error message. The repository is also setting a LiveData property for an error message, which should be managed at a higher level.

Backend Blunders: Business Logic in Spring Boot

On the backend side, things can go just as wrong. Let’s dive into a Spring Boot example:

@RestController
public class OrderController {

@PostMapping("/placeOrder")
public ResponseEntity<String> placeOrder(@RequestBody Order order) {
// Business logic in the controller - nooo!
double total = order.getTotal();
if (order.getCustomer().isGold()) {
total = total * 0.9; // Apply 10% discount for Gold
}
order.setTotal(total);
orderService.saveOrder(order);
return ResponseEntity.ok("Order placed successfully");
}
}

In this example, the OrderController is bogged down with business logic (Gold discount ). This logic should reside in a service layer, not in the controller.

Wrapping Up

Understanding where to place your domain, business, and application logic is crucial for maintaining a clean and scalable codebase. Here are the key takeaways:

  • Domain Logic belongs in your domain models.
  • Business Logic should reside in service layers or business-specific classes.
  • Application Logic is all about handling user interaction and should not contain business rules.

Hopefully, these examples (and anti-examples) help clarify the distinctions. And remember, even seasoned engineers like us can get confused. The key is to keep learning and refactoring our code to make it better.

Happy coding, and may your logic always be in the right place!

--

--