Your Framework Is Replaceable. Your Architecture Is Not.
Source: Dev.to
Frameworks are transient by nature.
They emerge, gain traction, dominate discussions for a few years, and are eventually replaced or fundamentally re‑worked. This is not a flaw of frontend development; it is a natural consequence of rapid innovation in tooling, browsers, and developer expectations.
The mistake many teams make is not choosing the “wrong” framework, but assuming that the framework they choose today will still shape their codebase in the same way tomorrow. What should remain stable over time is the business logic of an application: the rules, validations, workflows, and constraints that represent real‑world requirements. Unfortunately, in many frontend projects these two concerns are tightly intertwined.
The Problem: Framework Coupling
In theory, a framework is just a tool and business rules are implemented inside components. At first this feels efficient—everything is close together, the mental overhead is low, and progress is fast. Over time, however, the framework stops being a replaceable detail and starts defining how the system works at its core. Changing the framework then becomes a fundamental rewrite rather than a simple UI tweak.
Typical scenarios that expose this coupling:
- Migrating to a new framework version
- Introducing a second UI (e.g., mobile or desktop)
- Reusing logic in a different environment
- Improving test coverage in a meaningful way
When the business logic is entangled with framework concepts, extracting it requires pulling in component rendering, lifecycle simulation, and extensive mocking. Small changes ripple through the UI layer even when the underlying rules remain unchanged.
Guiding Principle: Separate by Rate of Change
A useful architectural principle is to separate concerns based on their expected rate of change:
- Frameworks change quickly.
- UI paradigms evolve.
Guideline: The UI layer coordinates. The domain layer decides.
- UI layer – handles user interaction and presentation. It gathers input, invokes application logic, and renders outcomes. It should not contain validation rules, business decisions, or side‑effects.
- Domain layer – contains the core business rules and workflows, independent of any UI technology.
Example: UI vs. Domain Code
UI‑centric implementation (framework code)
// UI component (framework code)
component CheckoutForm {
state email = ""
onSubmit() {
if (!email.includes("@")) {
showError("Invalid email")
return
}
const response = http.post("/checkout", { email })
if (response.ok) {
navigate("/success")
} else {
showError("Checkout failed")
}
}
render() { /* ... */ }
}
Problems:
- Validation and checkout workflow are locked into component lifecycle, framework state management, and UI‑level error handling.
- Reusing or testing the logic requires the framework to be present.
Refactored separation (plain domain code)
// Domain / application layer (plain code, no framework)
function validateEmail(email) {
if (!email) return { error: "Email is required" }
if (!email.includes("@")) return { error: "Invalid email" }
return { ok: true }
}
async function submitCheckout(email, httpClient) {
const validation = validateEmail(email)
if (validation.error) return validation
const response = await httpClient.post("/checkout", { email })
if (!response.ok) return { error: "Checkout failed" }
return { ok: true }
}
// UI component (framework code) using the domain layer
component CheckoutForm {
state email = ""
state error = null
state loading = false
async onSubmit() {
loading = true
error = null
const result = await submitCheckout(email, http)
loading = false
if (result.error) {
error = result.error
} else {
navigate("/success")
}
}
render() { /* ... */ }
}
Benefits:
- The domain functions are callable from anywhere (UI, CLI, background jobs, tests).
- They are testable without rendering a component.
- They remain reusable even if the UI framework changes, keeping the UI slim and replaceable.
Why This Matters Beyond Testability
- Risk reduction: Lower migration risk and refactoring cost when frameworks evolve.
- Longevity: A longer useful lifespan for the codebase.
- Clarity: Business behavior lives in one place, separate from UI mechanics, improving overall code quality.
Framework upgrades are often assumed to be incremental, but many frameworks change their mental models dramatically. Architecture, therefore, is less about elegance and more about managing risk.
Practical Reflection
If you were forced to replace your frontend framework in five years, ask yourself:
- Which parts of your system would survive unchanged?
- Which parts would need to be rewritten from scratch?
If the answer is “almost everything needs to be rewritten,” the framework has become your architecture—a choice worth reconsidering.