Scalable Frontend Architecture & Data Flow
- Dominant language
- JavaScript
- Stars
- 400
- Forks
- 89
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 149
Description
# Reasoning
To support a growing, distributed engineering team, we need to formalize our tools and frameworks to ensure scalable, high-velocity development.
## Key Architectural Concepts: Scalable Frontend Framework
### 1. The "Hydrate-Before-Route" Pattern
Eliminates race conditions by ensuring the application state is fully populated before the UI layer is rendered.
* **Blocking Bootstrap:** The router intercepts navigation and waits for a centralized `bootstrap()` or `hydrateTeam()` action.
* **Atomic Readiness:** The `isHydrated` flag serves as a global gatekeeper; no component setup logic runs until all required domain data (RBAC, Team Context, Feature Flags) is confirmed in the store.
* Note on Tiered Hydration: This concept can be extended by categorizing data into Critical and Non-Critical tiers. While mandatory domain data (like Permissions) must block the entire application to ensure security and core functionality, other data sets can be flagged for "partial blocking." This allows for a progressive loading strategy where the shell renders immediately using skeleton screens while secondary data fetches in the background.
### 2. Guard-Based Access Control (GBAC)
Moves security enforcement from the "View" layer to the "Router" layer to ensure consistency across the application.
* **Declarative Metadata:** Access requirements are defined in the route configuration rather than hardcoded in component logic.
* **Context-Aware Guards:** Registry-based guards dynamically resolve parameters (e.g., `team_id`) from the URL to validate permissions against pre-loaded store data.
* **Elimination of UI Flickers and "Layout Shifts"**: By resolving permissions before the component mounts, you prevent "Flash of Unauthorized Content" (FOUC) and the jarring transition where a user sees a page for a split second before being redirected.
* **Unified Loading State Management**: Instead of every nested component triggering its own independent loading spinner, the router manages a single, global transition state. This removes the "plethora of loaders" and provides a smooth, predictable navigation experience.
* **Reduction of Component Logic Clutter**: Decouples business logic from security logic. Components no longer need to "know" about authentication states or permission strings; they can assume that if they are rendered, the user has the necessary clearance and data at their disposal.
* **Prevention of Redundant Redirect Loops**: Centralizing logic at the entry point stops the "waterfall" effect of nested components triggering multiple, competing redirects. The router evaluates the entire branch once, ensuring a single, authoritative redirection if requirements aren't met.
* **Atomic Permission Resolution**: Moves from a distributed model (checking permissions multiple times across the tree) to an atomic model. Guards validate the user's scope against the specific route parameters (like `team_id`) once, ensuring consistency across the entire view hierarchy.
### 3. Service-Oriented Business Logic
Separates "Product Functionality" from "Framework Implementation" for maximum reliability and testability.
* **Stateless Services:** Dedicated classes/functions for API integration and data transformation (DTO-to-Domain mapping).
* **Store-Agnosticism:** Services return data and remain unaware of Pinia/Vue, making them purely unit-testable in isolation with Vitest.
### 4. Modular State with Pinia
Transitioning from Vuex to Pinia to leverage a modern, type-safe, and distributed state model. (Vuex reached EOL)
* **Domain Segregation:** State is split into logical modules (Contextual, UI, Account, Product) that can be owned by different feature teams.
* **Reduced Complexity:** Removes the "Mutation" layer, lowering the barrier to entry for new developers and simplifying the mental model.
### 5. The "Golden Path" Data Flow
A normalized development pattern that ensures predictability as the engineering team scales.
1. **Component:** Dispatches an Action (no direct service/API client imports).
2. **Store Action:** Orchestrates the flow and calls a Service.
3. **Service:** Fetches data via the HTTP Client and maps it to a Domain Model.
4. **Store State:** Updates reactively.
5. **Component:** Reflects the updated state automatically.
This "Golden Path" approach is effective because it enforces a **strict separation of concerns**, ensuring the UI remains a pure reflection of state rather than a tangled web of data-fetching logic. By decoupling components from external services and centralizing data within the Store, you create a "single source of truth" that makes information **instantly accessible across the entire application**, eliminating the need for complex prop-drilling or redundant API calls when moving between different pages or contexts.
Then, **Global Availability** becomes a side effect because once data is fetched, it resides in the global state, meaning any component (regardless of where it sits in the hierarchy) can reactively consume (or act upon) that data without re-triggering network requests.
Implementing the **"Golden Path"** establishes a formal contract between our UI and our data layer. By decoupling side effects from the component lifecycle, we eliminate the fragility of inline fetch calls and manually managed state transitions. This architecture creates a predictable, unidirectional flow that transforms our Store into a **high-availability global cache**, ensuring that once data is hydrated, it is immediately accessible to any consumer in the tree, regardless of the current route or context.
Integrating [**TanStack Query**](https://tanstack.com/query/v5/docs/framework/vue/quick-start) into this flow is the natural evolution of this pattern. It allows us to offload the heavy lifting of server-state management (such as cache invalidation, background revalidation, and request deduplication) to a robust, declarative framework. Instead of writing boilerplate to sync local state with the API, our **Service layer** becomes a specialized orchestrator that keeps our global state perpetually "fresh," allowing the team to focus on shipping features rather than debugging race conditions or stale data.
### 6. Decoupled Testing Strategy
Concerns are separated to enable efficient, parallelized testing pipelines:
* **Logic Layer:** Unit tests for **Services** (mocking the HTTP client).
* **Orchestration Layer:** Integration tests for **Pinia Actions** (mocking Services).
* **UI Layer:** Component tests for **Vue files** (mocking the Store state).
### 7. Logic Placement & Functional Segregation
To ensure the codebase remains maintainable as the engineering team scales, we enforce a strict separation between **Domain Logic**, **State Orchestration**, and **Representational Logic**. This prevents the "Leaky Abstraction" anti-pattern where business rules bleed into the UI.
#### **The Service Layer: "Domain & Business Logic"**
This is the primary residence for all Product and Business functionality. Services are the "Source of Truth" for how the business operates.
* **Responsibilities:**
* **Data Normalization:** Mapping raw API DTOs into Domain Models.
* **Business Rules:** Validations, complex calculations, and permission logic (RBAC).
* **Encapsulation:** Services consume the persistent **HTTP Client** but remain stateless regarding application state.
* **Architectural Goal:** Logic in this layer must be testable in a headless environment (Vitest) without requiring the Vue reactivity system or a Store instance.
#### **The Store (Pinia): "State Orchestration"**
The Store acts as the "Middle Manager." It does not decide *how* a business rule works; it decides *when* to execute it and where to save the result.
* **Responsibilities:**
* **Asynchronous Flow:** Managing the lifecycle of a request (Loading -> Success/Error).
* **Side Effects:** Triggering global events, such as toast notifications or logging.
* **Cross-Domain Coordination:** Orchestrating calls between multiple services (e.g., fetching a User profile and then fetching their specific Team configuration).
* **Architectural Goal:** Maintain a clean, reactive snapshot of the application state.
#### **The Component Layer: "Representational Logic"**
Components are strictly consumers of state and triggers for actions. They should be as "dumb" as possible regarding the product’s business rules.
* **Responsibilities:**
* **Interaction State:** Local UI toggles (e.g., `isModalOpen`, `activeTab`).
* **Event Mapping:** Translating user inputs (clicks, form submits) into Store dispatches.
* **Formatting:** UI-specific data prep (e.g., converting a raw date into a localized string for display).
* **Architectural Goal:** High reusability and low cognitive load. A developer should be able to change a business rule in a Service without ever touching a `.vue` file.
## Technical Benefits & Rationale
- **Predictability**: isHydrated ensures that by the time a component's setup() runs, the data it needs is guaranteed to be in the store
- **Decoupled Logic**: Services can be reused across different stores, components or even in a CLI tool/SSR environment.
- **Declarative Security**: Access control is managed at the router level, preventing "flickers" of unauthorized content before a redirect occurs.
- **Performance**: Parallelized bootstrapping and the use of Pinia over Vuex reduce both network latency and runtime overhead.
- **Standardized Workflow**: Enforces a strict, unidirectional data flow (Component → Store → Service) that reduces cognitive load and speeds up onboarding.
- **Elimination of Race Conditions**: The Hydrate-Before-Route pattern ensures the application state is fully populated before the UI renders, removing "Flash of Unauthorized Content" (FOUC).
- **Reduced Logic Leakage**: By moving business rules into stateless Services, we prevent "Leaky Abstractions" where complex logic clutters the UI layer.
- **Headless Testability**: Decoupling logic from the Vue framework allows for high-speed unit testing of business rules in isolation (Vitest) without DOM overhead.
- **State as a Global Cache*: The "Golden Path" transforms the store into a high-availability cache, eliminating redundant API calls and complex prop-drilling across the component tree.
- **Superior User Experience**
- **Unified Loading States**: Centralizing transitions at the Router layer replaces fragmented "spinner hell" with a smooth, predictable navigation experience.
- **Atomic Security**: Guard-Based Access Control (GBAC) evaluates permissions once at the entry point, preventing redundant redirect loops and ensuring a secure, consistent UI state.
- **Future-Ready Usability**: Centralizing state and logic opens the door for high-order "App-Like" features that are difficult to implement in component-heavy builds.
- This includes Global Undo/Redo, Optimistic UI Updates (rendering changes before the server confirms), Cross-Tab Synchronization, and Predictive Prefetching—features that transform a standard web tool into a high-performance, resilient platform.
Contributor guide
Assessment
This issue has not been assessed yet.