Separation of Concerns: UI vs. Business Logic
By the end of this lesson, you'll be able to:
- Distinguish UI rendering/interaction logic from genuine business logic
- Explain why mixing the two in one place hurts maintainability and testability
- Extract business logic into a reusable, independently testable location
Prerequisites: Module 15: "Exercise: Diagnose Realistic Broken Components"
What Is Separation of Concerns?
A component's JavaScript controller class should focus on UI concerns — reactive state, event wiring, rendering decisions. Genuine business logic — validation rules, calculations, data transformation — belongs somewhere more reusable: either an Apex service class (server-side, Lesson 2) or a plain, importable JavaScript utility module (client-side, using Module 4's import/export).
Why Mixing Them Hurts
Business logic embedded directly inside a component's event handler is hard to reuse in a second component that needs the same rule, and hard to unit test in isolation from the component's rendering — a Jest test (Module 14) ends up needing to simulate a whole component just to verify a calculation that has nothing to do with rendering at all.
A Before/After Example
// Before — business logic buried inside a UI event handler
handleApplyDiscount() {
let discount = 0;
if (this.orderTotal > 1000) {
discount = this.orderTotal * 0.1;
} else if (this.orderTotal > 500) {
discount = this.orderTotal * 0.05;
}
this.finalTotal = this.orderTotal - discount;
}
// discountUtils.js — reusable, independently testable
export function calculateDiscount(orderTotal) {
if (orderTotal > 1000) return orderTotal * 0.1;
if (orderTotal > 500) return orderTotal * 0.05;
return 0;
}
// After — the component delegates the actual calculation
import { calculateDiscount } from './discountUtils';
handleApplyDiscount() {
const discount = calculateDiscount(this.orderTotal);
this.finalTotal = this.orderTotal - discount;
}
calculateDiscount can now be tested directly with plain values, reused by any other component that needs the same rule, and changed in one place rather than several.
Where This Applies Server-Side Too
The same principle applies to Apex controllers exposed via @AuraEnabled (Module 7) — they should stay thin, delegating real business logic to separate service classes rather than embedding it directly in the LWC-facing method. Lesson 2 covers this exact layering in depth.
Exercise
Extract this inline validation logic into a standalone, exported isValidEmail function.
Show hint
Move the logic out of the handler into its own module-level function.
Exercise
Challenge: explain, as a comment, why extracting calculateDiscount makes it easier to unit test than leaving it inline in handleApplyDiscount.
Show hint
Think about what a test would need to set up in each case.
Separation of Concerns: UI vs. Business Logic Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A component's controller class should focus on UI state and event wiring — genuine business rules belong somewhere reusable and independently testable, not buried inside a click handler.