Frontend Architecture Refactoring Plan
The purpose of this plan is to outline the architectural code smells present in the current React Native (Expo) frontend and propose a strategy to refactor and clean up the code.
1. Circular Dependencies and Inline Requires
Problem:
Core modules like apiFetch.ts, authStore.ts, and App.tsx are using inline require() calls inside functions (e.g., const { useAuthStore } = require("@/stores/authStore");) to break circular dependencies. This is a severe anti-pattern that bypasses static analysis, makes testing difficult, and indicates tightly coupled components.
Proposed Solution (Inversion of Control):
- Remove all inline
require() calls.
- Break the dependency between
apiFetch (HTTP client) and authStore (State management) by using a callback/interceptor pattern.
- E.g., Expose a
setAuthInterceptor(onUnauthorized: () => void, onRefresh: () => Promise<boolean>) method in apiFetch.ts.
- In
App.tsx or an initialization script, link the store actions to the interceptor.
2. Fat UI Components & Mixed Concerns
Problem:
UI components, particularly LoginScreen.tsx (and likely others like ProfileScreen.tsx and RegisterForm.tsx), contain heavy business logic. LoginScreen.tsx is manually constructing API requests (fetch(...)), parsing responses, handling Stardbi mappings, and orchestrating Zustand state updates.
Proposed Solution (Thin Components):
- Extract all API interaction and complex business logic into dedicated services (e.g.,
services/authService.ts) or encapsulate them fully within the Zustand stores.
LoginScreen.tsx should only handle local form state (username, password), validation, and invoke a single function like authStore.login(username, password).
3. Monolithic React Query File
Problem:
app/api/queries.ts is a massive monolithic file containing every React Query hook for the entire application (Tasks, Gamification, Users, Admin, etc.). This makes it hard to maintain and prone to merge conflicts.
Proposed Solution (Feature-based Modularization):
- Break down
queries.ts into a queries/ directory grouped by domain context:
queries/authQueries.ts
queries/taskQueries.ts
queries/gamificationQueries.ts
queries/userQueries.ts
queries/adminQueries.ts
- Keep a centralized
queryKeys.ts factory if necessary, or keep keys local to their domain files.
4. Inconsistent API Usage
Problem:
While apiFetch.ts provides an authenticated fetch wrapper, there are scattered raw fetch() calls in the codebase (e.g., authStore.logout uses raw fetch and constructs Authorization headers manually; LoginScreen.tsx uses raw fetch for Stardbi login).
Proposed Solution:
- Strictly enforce the use of
apiFetch for all backend interactions.
- If unauthenticated requests are needed, provide a clear standard (e.g.,
apiFetch(..., { requireAuth: false })) rather than falling back to standard fetch.
5. Architectural Recommendations (Addressing Open Questions)
Based on the nature of SwipeLab—particularly its moderate-to-high complexity (researcher vs. user modes, gamification, swiping, superadmin flows) and its backend architecture—here are my recommendations for the refactoring direction:
Recommendation A: Adopt a Feature-Based (Domain-Driven) Folder Structure
Currently, the frontend separates files by technical layer (app/components, app/screens, app/stores, app/api). I recommend moving to a Feature-Based (Modular) structure.
Trade-offs:
- Technical Layer (Current): Easier for small apps, but as SwipeLab grows, modifying the "Task" feature requires jumping between
screens/TaskScreen, stores/taskStore, api/queries.ts, and components/TaskCard. This reduces cohesion and increases cognitive load.
- Feature-Based (Recommended): Groups everything by domain (e.g.,
app/features/auth, app/features/tasks, app/features/gamification).
- Pros: High cohesion. An engineer working on authentication only needs to look in
features/auth. This also perfectly mirrors your backend's Modular Hexagonal/DDD structure, creating a unified mental model across the stack.
- Cons: Requires a significant initial file migration. You will also need a
features/core or features/shared folder for global UI components (like Buttons) or global hooks.
Recommendation B: Testing Strategy (Test-Driven Refactoring)
The SwipeLab rules strictly enforce test coverage for modifications. Since this refactor touches critical infrastructure (Auth, API fetching), regressions are a high risk.
Trade-offs:
- Relying solely on E2E (Playwright): You already have Playwright tests (e.g.,
settings.spec.ts). While excellent for ensuring the app works holistically, E2E tests are slow and can be brittle during structural refactors.
- Adding Unit Tests (Jest + React Native Testing Library): Fast and isolated. Testing the new
authStore or apiFetch interceptors in isolation guarantees that the foundational logic is sound before the UI even renders.
Recommended Approach:
- Write Unit Tests First: Before touching
authStore.ts or apiFetch.ts, ensure unit tests exist for them. When we break the circular dependencies, these tests will turn green to confirm success.
- Lean on Playwright for UI: Let the existing E2E tests serve as our safety net to ensure we didn't break routing or screen rendering when moving components into the feature-based structure.
Proposed File Changes
Stores & Services
[MODIFY] authStore.ts
[NEW] authService.ts
API Layer
[MODIFY] apiFetch.ts
[DELETE] queries.ts
[NEW] queries/taskQueries.ts
[NEW] queries/userQueries.ts
[NEW] queries/gamificationQueries.ts
Screens
[MODIFY] LoginScreen.tsx
[MODIFY] ProfileScreen.tsx
[MODIFY] App.tsx
Verification Plan
Automated Tests
- Run
npm run test or Playwright end-to-end tests (npm run test:e2e) to verify that the core flows (login, tasks, profile) remain intact after the refactor.
Manual Verification
- Manually test local and external (researcher) login flows.
- Verify that session expiration and token refresh still work correctly without circular dependency issues.
Frontend Architecture Refactoring Plan
The purpose of this plan is to outline the architectural code smells present in the current React Native (Expo) frontend and propose a strategy to refactor and clean up the code.
1. Circular Dependencies and Inline Requires
Problem:
Core modules like
apiFetch.ts,authStore.ts, andApp.tsxare using inlinerequire()calls inside functions (e.g.,const { useAuthStore } = require("@/stores/authStore");) to break circular dependencies. This is a severe anti-pattern that bypasses static analysis, makes testing difficult, and indicates tightly coupled components.Proposed Solution (Inversion of Control):
require()calls.apiFetch(HTTP client) andauthStore(State management) by using a callback/interceptor pattern.setAuthInterceptor(onUnauthorized: () => void, onRefresh: () => Promise<boolean>)method inapiFetch.ts.App.tsxor an initialization script, link the store actions to the interceptor.2. Fat UI Components & Mixed Concerns
Problem:
UI components, particularly
LoginScreen.tsx(and likely others likeProfileScreen.tsxandRegisterForm.tsx), contain heavy business logic.LoginScreen.tsxis manually constructing API requests (fetch(...)), parsing responses, handling Stardbi mappings, and orchestrating Zustand state updates.Proposed Solution (Thin Components):
services/authService.ts) or encapsulate them fully within the Zustand stores.LoginScreen.tsxshould only handle local form state (username, password), validation, and invoke a single function likeauthStore.login(username, password).3. Monolithic React Query File
Problem:
app/api/queries.tsis a massive monolithic file containing every React Query hook for the entire application (Tasks, Gamification, Users, Admin, etc.). This makes it hard to maintain and prone to merge conflicts.Proposed Solution (Feature-based Modularization):
queries.tsinto aqueries/directory grouped by domain context:queries/authQueries.tsqueries/taskQueries.tsqueries/gamificationQueries.tsqueries/userQueries.tsqueries/adminQueries.tsqueryKeys.tsfactory if necessary, or keep keys local to their domain files.4. Inconsistent API Usage
Problem:
While
apiFetch.tsprovides an authenticated fetch wrapper, there are scattered rawfetch()calls in the codebase (e.g.,authStore.logoutuses rawfetchand constructs Authorization headers manually;LoginScreen.tsxuses rawfetchfor Stardbi login).Proposed Solution:
apiFetchfor all backend interactions.apiFetch(..., { requireAuth: false })) rather than falling back to standardfetch.5. Architectural Recommendations (Addressing Open Questions)
Based on the nature of SwipeLab—particularly its moderate-to-high complexity (researcher vs. user modes, gamification, swiping, superadmin flows) and its backend architecture—here are my recommendations for the refactoring direction:
Recommendation A: Adopt a Feature-Based (Domain-Driven) Folder Structure
Currently, the frontend separates files by technical layer (
app/components,app/screens,app/stores,app/api). I recommend moving to a Feature-Based (Modular) structure.Trade-offs:
screens/TaskScreen,stores/taskStore,api/queries.ts, andcomponents/TaskCard. This reduces cohesion and increases cognitive load.app/features/auth,app/features/tasks,app/features/gamification).features/auth. This also perfectly mirrors your backend's Modular Hexagonal/DDD structure, creating a unified mental model across the stack.features/coreorfeatures/sharedfolder for global UI components (like Buttons) or global hooks.Recommendation B: Testing Strategy (Test-Driven Refactoring)
The SwipeLab rules strictly enforce test coverage for modifications. Since this refactor touches critical infrastructure (Auth, API fetching), regressions are a high risk.
Trade-offs:
settings.spec.ts). While excellent for ensuring the app works holistically, E2E tests are slow and can be brittle during structural refactors.authStoreorapiFetchinterceptors in isolation guarantees that the foundational logic is sound before the UI even renders.Recommended Approach:
authStore.tsorapiFetch.ts, ensure unit tests exist for them. When we break the circular dependencies, these tests will turn green to confirm success.Proposed File Changes
Stores & Services
[MODIFY] authStore.ts
[NEW] authService.ts
API Layer
[MODIFY] apiFetch.ts
[DELETE] queries.ts
[NEW] queries/taskQueries.ts
[NEW] queries/userQueries.ts
[NEW] queries/gamificationQueries.ts
Screens
[MODIFY] LoginScreen.tsx
[MODIFY] ProfileScreen.tsx
[MODIFY] App.tsx
Verification Plan
Automated Tests
npm run testor Playwright end-to-end tests (npm run test:e2e) to verify that the core flows (login, tasks, profile) remain intact after the refactor.Manual Verification