youtubeJanuary 11, 2026
Functional Core, Imperative Shell - Introduction and Goals
Pure functions that receive arguments and return new values (functional core) should handle business logic, while a thin imperative shell manages state mutation and UI integration.
Key Takeaways
- Separate business logic from UI logic — The functional core contains pure functions that transform data; the imperative shell handles DOM updates and user input
- Avoid global mutation — Functions like
makeMoveshould receive the current game state as an argument and return a new state, not mutate global variables - Function signatures reveal intent — When a function takes no arguments and returns nothing, you can't understand what it does without reading the implementation
- Pure functions are testable — When output depends only on input, tests become straightforward assertions
The Problem
The video examines a tic-tac-toe game with typical issues:
makeMovemutates a specific row/column in a globalboardsarrayundotakes no arguments, returns nothing — impossible to understand from the signature- Business logic and reactivity are intertwined in a single composable
The Solution
graph TD
subgraph Shell["Imperative Shell (Vue)"]
DOM[DOM Updates]
Input[User Input]
end
subgraph Core["Functional Core"]
MakeMove[makeMove]
Undo[undo]
Redo[redo]
end
Input --> Core
Core -->|new state| Shell
Shell -->|current state| Core
Functional Core: Pure functions that take current state as input and return new state. No side effects, no global variables.
Imperative Shell: A thin Vue integration layer that:
- Receives user input
- Passes state to pure functions
- Updates reactive state with returned values
Refactored Approach
Instead of:
// Bad: mutates global, returns nothing
function undo() {
currentMove--
// mutates board somehow
}
Use:
// Good: pure function, clear contract
function undo(currentState) {
return {
...currentState,
currentMove: currentState.currentMove - 1,
board: currentState.history[currentState.currentMove - 1]
}
}
Connections
- 12-design-patterns-in-vue — Pattern #2 "Thin Composables" describes the same architecture: pure functions for logic with a minimal reactivity wrapper
- 13-vue-composables-tips — Tip #5 applies this principle: "Separate business logic from reactivity"