Where Does Your Business Logic Belong?
In the world of app development, “business logic” is the backbone of your application—it’s what makes it tick. But where you place this logic can make a big difference in how well your app works and how easy it is to maintain. Let’s dive in with some real-world examples. 🏊
What is Business Logic? 🤔
Think of business logic as the rules that your app needs to follow to do its job correctly.
For instance, in a banking app 🏦, when someone wants to send money to a friend, there are several steps:
1. Check if the person sending the money (the sender) has enough in their account.
2. Take the money out of the sender’s account (debit).
3. Put the money into the friend’s account (credit).
4. Make sure both accounts show the new amounts.
These steps are the “business logic” for transferring money.
Easy Apps
With easy apps, it’s like having a small toolbox. You can keep your tools (business logic) right there where you use them.
Here’s a simple To-Do app 📝 code snippet where the business logic is right in the controller:
@Controller public class TaskController { private final TasksRepository taskRepo; @PostMapping("/tasks") public String create(@Valid Task task) { if (task.isValid()) { taskRepo.save(task); } else { // Handle invalid task 🚫📝 } return "taskView"; } }
Complex Apps
For bigger apps, your toolbox gets too full. You need a shed (services) to keep everything organized.
Look at this example for a banking transfer:
@Service public class TransferService { @Autowired private AccountRepository accountRepository; public void transfer(long fromId, long toId, BigDecimal amount) { Account from = accountRepository.findById(fromId).orElseThrow(); Account to = accountRepository.findById(toId).orElseThrow(); if (from.hasSufficientFunds(amount)) { from.debit(amount); to.credit(amount); accountRepository.save(from); accountRepository.save(to); } else { // Handle insufficient funds 👎💔 } } }
And the controller just asks the service to do the work:
@Controller public class TransferController { @Autowired private TransferService transferService; @PostMapping("/transfer") public String transfer(@RequestBody TransferDto dto) { transferService.transfer(dto.getFromAccountId(), dto.getToAccountId(), dto.getAmount()); return "redirect:/accounts"; } }
Advanced Apps and DDD
For really complex apps, Domain-Driven Design (DDD) comes into play. Imagine building a skyscraper 🏗️ instead of a shed. You need a detailed blueprint (domain model).
In DDD, you:
1. Define what each part of your app is responsible for (domain entities and value objects).
2. Put the rules (logic) into these parts.
3. Use special code (repositories) to get and save data.
4. Create services that handle specific tasks (use case services).
In our banking app, accounts are entities that know how to handle their own business:
@Entity public class Account { @Id private Long id; private BigDecimal balance; private BigDecimal adminFee; // Constructors, getters, and setters are omitted for brevity. public void deposit(BigDecimal amount) { validateAmountNonNegative(amount); this.balance = this.balance.add(amount); } public void withdraw(BigDecimal amount) throws InsufficientFundsException { validateAmountNonNegative(amount); if (hasSufficientFunds(amount)) { this.balance = this.balance.subtract(amount); } else { throw new InsufficientFundsException("Insufficient funds for withdrawal."); } } public void applyAdminFee() throws InsufficientFundsException { if (this.balance.compareTo(adminFee) < 0) { throw new InsufficientFundsException("Insufficient funds to cover the admin fee."); } this.balance = this.balance.subtract(adminFee); } private void validateAmountNonNegative(BigDecimal amount) { if (amount.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException("Amount must be positive."); } } private boolean hasSufficientFunds(BigDecimal amount) { return this.balance.compareTo(amount) >= 0; } // Additional business logic and helper methods can be added as needed. }
Moving the logic into domain entities and services gives us:
- Reusable code: You can use the same service for different parts of your app.
- Clear roles: Each part of your app does a specific job.
- Problem-focused design: Your code structure matches the real-world problem you’re solving.
Conclusion
In short, for simple tasks, you might keep your business logic in controllers. But as your app grows and the problems get more complex, you’ll want to use services and domain models to keep everything running smoothly. Where will you put your business logic in your next project? 🛠️ Let’s build something amazing together!
One Response
Like it. and some times you will have huge details regarding the business requirements, even in this case it will be nicer not to make the service layer bigger. we can present extra layer to handle and decrease the business details will be applied to the service so the modules became readable and solid.