Skip to content

Architecture

Mindhyv follows a modular monolith architecture within a SvelteKit application. Each feature area is encapsulated in its own module under src/lib/modules/, with shared infrastructure in src/lib/.

src/
├── routes/ # SvelteKit file-based routing
│ ├── (app)/ # Authenticated app routes
│ ├── (auth)/ # Authentication routes (public)
│ ├── (goals)/ # Goal selection routes
│ ├── (onboarding)/ # Onboarding flow
│ ├── (public)/ # Public-facing pages
│ └── api/ # API endpoints
├── lib/
│ ├── modules/ # Feature modules (self-contained)
│ ├── components/ # Shared UI components
│ ├── services/ # Shared services
│ ├── stores/ # Shared state stores
│ ├── types/ # Shared type definitions
│ ├── schemas/ # Shared validation schemas
│ ├── hooks/ # Svelte hooks
│ ├── utils/ # Utility functions
│ ├── data/ # Reference data (countries, languages, etc.)
│ └── config.ts # App configuration

Each module in src/lib/modules/ follows a consistent internal structure:

modules/{module-name}/
├── components/ # Svelte components specific to this module
├── types/ # TypeScript interfaces and enums
├── stores/ # Svelte 5 state managers (singletons)
├── services/ # Business logic and Firebase operations
├── schemas/ # Valibot validation schemas
├── utils/ # Module-specific utilities
└── index.ts # Public exports

All state managers use a Singleton pattern with Svelte 5 runes:

class ManagerName {
private static instance: ManagerName | null = null;
// Reactive state using Svelte 5 runes
private _data = $derived(firekitDoc('collection/path'));
public data = $derived(this._data?.data);
private constructor() {}
public static getInstance(): ManagerName {
if (!ManagerName.instance) {
ManagerName.instance = new ManagerName();
}
return ManagerName.instance;
}
}
export const managerName = ManagerName.getInstance();

Services encapsulate Firebase operations and business logic:

export class ServiceName {
private readonly basePath: string;
constructor(userId: string) {
this.basePath = `users/${userId}`;
}
async createItem(data: ItemType): Promise<void> {
// Firestore operations via svelte-firekit
}
}

Each module exposes its public API through an index.ts barrel file:

import Component from "./components/component.svelte";
export { Component };
export { managerName } from "./stores/manager.svelte";
export type { TypeName } from "./types/types";

SvelteKit route groups organize pages by authentication and layout requirements:

GroupPathPurposeAuth Required
(auth)/sign-in, /sign-up, /forgot-passwordAuthentication pagesNo
(onboarding)/onboardingNew user setupYes
(goals)/goalsGoal selectionYes
(app)/dashboard, /hubs, /connect, etc.Main applicationYes
(public)/public-profile/[id]Public-facing pagesNo
api/api/blogs, /api/stripe/*Server API endpointsVaries
User Action → Svelte Component → Store (Manager) → Service → Firebase (Firestore/Auth/Storage)
Reactive UI Update ($derived)
  1. Components dispatch actions to stores
  2. Stores hold reactive state via $derived from firekitDoc/firekitCollection
  3. Services perform Firebase CRUD operations
  4. Firebase triggers real-time updates back to stores via svelte-firekit bindings
  5. Components reactively re-render via Svelte 5 runes
Request → +layout.ts → AuthGuard → CustomGuard (registration approved?)
CustomGuard (onboarding completed?)
Route Component
  • checkRegistrationApproved() - Redirects to waiting-for-approve page
  • checkOnboardingCompleted() - Redirects to onboarding page