Development2024-03-2015 min čteníSnapTime Team

🧱 Architektura moderní time tracking aplikace (Next.js + Firebase)

Kompletní technický průvodce pro vývojáře: jak navrhnout škálovatelnou time tracking aplikaci s Next.js 14, Firebase a TypeScript. Včetně real-world příkladů z SnapTime.

🧱 Architektura moderní time tracking aplikace (Next.js + Firebase)

Budování moderní time tracking aplikace není jen o tom napsat kód, který "nějak funguje". Jde o vytvoření škálovatelné, maintainabilní a performantní architektury, která bude růst s vaším produktem i týmem.

V tomto technickém deep-dive si rozebereme konkrétní architekturní rozhodnutí při vývoji SnapTime – time tracking aplikace s ~24,000 řádků kódu, která zvládá komplexní týmové scénáře a real-time synchronizaci.

📊 SnapTime v číslech

Velikost projektu:

  • 91 TypeScript/JavaScript souborů
  • ~23,898 řádků kódu
  • 905 celkových souborů
  • 1.1MB finální bundle size

Technické metriky:

  • Složitost: 3/5 (střední)
  • Maintainability: 4/5 (vysoká)
  • Scalability: 4/5 (vysoká)
  • Performance: 4/5 (vysoká)

🎯 Proč Next.js + Firebase pro time tracking?

Při výběru tech stacku pro SnapTime jsme měli několik požadavků:

  • Real-time synchronizace – týmy potřebují vidět změny okamžitě
  • Komplexní autentizace – individuální uživatelé i týmové organizace
  • SEO optimalizace – marketing pages + blog musí rankovat
  • Rychlý development – as a startup potřebujeme ship fast
  • Škálovatelnost – od 1 uživatele po enterprise týmy

Next.js 14 s App Routerem

Klíčové výhody pro SaaS aplikaci:

  • Hybrid rendering – SSR pro SEO, CSR pro interaktivitu
  • Server Actions – bezpečné mutations bez API routes
  • Streaming – progresivní loading komplexních dashboardů
  • Image optimization – automatická optimalizace avatarů a souborů
  • Middleware – auth protection na edge level

Firebase jako Backend-as-a-Service

Proč Firebase pro time tracking:

  • Firestore – real-time NoSQL s offline podporou
  • Firebase Auth – komplexní user management bez vlastního backendu
  • Security Rules – granulární permissions na database level
  • Firebase Functions – serverless computing pro background tasks
  • Analytics – built-in user tracking a performance monitoring

💡 Tech Stack Breakdown

Kategorie Technologie Verze Účel
Framework Next.js 14.1.0 Full-stack React framework
Language TypeScript 5.3.3 Type safety & developer experience
Backend Firebase 11.9.0 Database, Auth, Analytics
Styling Tailwind CSS 3.4.1 Utility-first CSS framework
State Zustand Latest Lightweight state management
Charts Chart.js + Recharts Latest Data visualization

🏗️ Architektura projektu

SnapTime používá modulární architekturu založenou na doménách. Každá složka odpovídá jedné business logice:

SnapTime/
├── app/                    # Next.js App Router
│   ├── api/               # API routes & Server Actions
│   │   ├── cron/         # Scheduled jobs (reminders)
│   │   ├── notifications/ # Email/push notifications
│   │   └── teams/        # Team management endpoints
│   ├── components/        # React components
│   │   ├── landing/      # Marketing components
│   │   ├── settings/     # Settings UI modules
│   │   └── seo/         # SEO components
│   ├── firebase/         # Firebase config & services
│   │   └── services/     # Domain-specific services
│   ├── hooks/            # Custom React hooks
│   ├── store/            # Zustand stores
│   └── types/            # TypeScript definitions
├── public/               # Static assets
└── scripts/             # Build & deployment scripts

Key Architectural Decisions

1. Service Layer Pattern

Veškerá komunikace s Firebase prochází přes service layer:

// firebase/services/projects.ts
export const projectService = {
  async createProject(data: CreateProjectData) {
    // Validation, transformation, Firestore operations
  },
  
  async getProjectsByTeam(teamId: string) {
    // Complex queries, caching, permissions
  }
}

2. Type-First Development

Všechny entity mají striktní TypeScript interface:

// types/time.ts
export interface TimeEntry {
  id: string;
  projectId: string;
  userId: string;
  startTime: Timestamp;
  endTime?: Timestamp;
  description: string;
  tags: string[];
  billable: boolean;
}

3. Optimistic UI s Error Handling

Time tracking vyžaduje okamžitou zpětnou vazbu:

const startTimer = async (projectId: string) => {
  // 1. Optimistic update
  setTimeEntries(prev => [...prev, tempEntry]);
  
  try {
    // 2. Server sync
    const entry = await timeService.startTimer(projectId);
    // 3. Replace with real data
    setTimeEntries(prev => prev.map(e => 
      e.id === tempEntry.id ? entry : e
    ));
  } catch (error) {
    // 4. Rollback on error
    setTimeEntries(prev => prev.filter(e => e.id !== tempEntry.id));
    toast.error('Failed to start timer');
  }
};

🔐 Security & Permissions

Time tracking aplikace obsahuje citlivá data. SnapTime implementuje multi-layer security:

1. Firestore Security Rules

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Time entries can only be read/written by owner
    match /timeEntries/{entryId} {
      allow read, write: if request.auth != null 
        && request.auth.uid == resource.data.userId;
    }
    
    // Team data accessible by team members
    match /teams/{teamId} {
      allow read: if request.auth != null 
        && request.auth.uid in resource.data.memberIds;
    }
  }
}

2. Next.js Middleware

// middleware.ts
export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    const token = request.cookies.get('session')?.value;
    
    if (!token) {
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }
}

📊 Performance Optimizations

Performance Čísla

1.1MB

Bundle Size

<200ms

Initial Page Load

95+

Lighthouse Score

Key Performance Strategies

1. Code Splitting & Lazy Loading

  • Route-based splitting – každá stránka je vlastní chunk
  • Component lazy loading – heavy komponenty (grafy, exporty) se načítají on-demand
  • Dynamic imports – jsPDF a XLSX se načítají až při exportu

2. Database Query Optimization

  • Index optimization – všechny časté queries mají composite indexy
  • Denormalization – duplikujeme data pro rychlejší reads
  • Pagination – time entries se načítají po dávkách
  • Real-time subscriptions – jen pro aktivní data, ne historical

3. Caching Strategy

Multi-level caching:

  • Next.js Static Generation – marketing pages a blog
  • Zustand persistence – user preferences a settings
  • Firebase offline cache – automatické offline caching
  • Browser cache – statické assety s long-term caching

🚀 Deployment & DevOps

CI/CD Pipeline

# Simplified deployment flow
git push origin main
  ↓
Vercel triggers build
  ↓
TypeScript compilation
  ↓
Next.js optimization
  ↓
Deploy to Vercel Edge
  ↓
Firebase functions deploy
  ↓
Update Firestore indexes

Monitoring & Analytics

  • Vercel Speed Insights – Real User Monitoring
  • Firebase Analytics – User behavior tracking
  • Firebase Crashlytics – Error reporting a crash detection
  • Custom logging – Business-specific metriky

🛠️ Development Workflow

Local Development Setup

# Quick start
git clone [repo]
cd SnapTime
npm install

# Environment setup
cp .env.example .env.local
# Add Firebase config

# Start dev server
npm run dev

# With Firebase emulators
npm run dev:firebase

Code Quality Tools

Automated quality checks:

  • TypeScript – Type checking na všech úrovních
  • ESLint – Code style a best practices
  • Prettier – Konzistentní formatting
  • Husky – Pre-commit hooks
  • Lint-staged – Incrementální linting

⚠️ Challenges & Lessons Learned

Firestore Query Limitations

Problém: Firestore nepodporuje complex queries jako SQL JOIN.

Řešení: Denormalizace dat a composite queries v application layer.

// Místo JOIN používáme denormalizaci
interface TimeEntry {
  id: string;
  projectId: string;
  projectName: string;    // Denormalized
  projectColor: string;   // Denormalized
  userId: string;
  // ...
}

Real-time Updates Performance

Problém: Příliš mnoho real-time subscriptions zabíjí performance.

Řešení: Selective subscriptions pouze pro aktivní data.

TypeScript Complexity

Problém: Firebase TypeScript types jsou sometimes inconsistent.

Řešení: Vlastní type wrappers a utility types.

📈 Scalability Considerations

Component Current Limit Scaling Strategy
Firestore Reads 1M/day per project Query optimization, caching
Real-time Connections 100K concurrent Connection pooling, selective subs
Vercel Functions 10s timeout Background jobs, queue system
Bundle Size 1.1MB current Tree shaking, code splitting

🔮 Future Architecture Improvements

Plánované optimalizace:

  • Edge Computing – Přesun business logiky na Vercel Edge Functions
  • GraphQL Layer – Unified API over Firebase pro complex queries
  • Redis Cache – External caching pro computed data
  • Micro-frontends – Možnost independent deployments
  • WebAssembly – Performance-critical calculations (time aggregations)

💡 Key Takeaways pro vývojáře

✅ Co se vyplatilo:

  • TypeScript everywhere – Saves hours of debugging
  • Service layer abstraction – Easy to test and modify
  • Optimistic UI – Great UX for time tracking
  • Firebase Security Rules – Database-level permissions
  • Modular architecture – Easy onboarding for new devs

❌ Co bychom dělali jinak:

  • Více unit testů – Testing Firebase apps je complex
  • GraphQL od začátku – Pro complex data fetching
  • Better error boundaries – Pro graceful degradation
  • Performance budgets – Automated bundle size monitoring

🚀 Začínáte s podobným projektem?

Starter Template

Pro rychlý start doporučujeme tento tech stack:

# Create Next.js app with TypeScript
npx create-next-app@latest my-time-tracker --typescript --tailwind --app

# Add essential dependencies
npm install firebase zustand react-hot-toast
npm install @types/node @headlessui/react @heroicons/react

# Initialize Firebase
npm install -g firebase-tools
firebase init

Essential Next.js Config

// next.config.js
module.exports = {
  experimental: {
    appDir: true,
  },
  images: {
    domains: ['firebasestorage.googleapis.com'],
  },
  webpack: (config) => {
    // Optimize bundle size
    config.resolve.fallback = { fs: false };
    return config;
  },
};

🔍 Chcete vidět kód v akci?

SnapTime je live example této architektury. Můžete si vyzkoušet funkcionalitu a vidět, jak se všechny tyto principy projevují v reálné aplikaci.

🆓 Pro vývojáře zdarma:

  • • Neomezené projekty
  • • Kalendářové zobrazení
  • • Export dat (PDF/CSV)
  • • Open-source inspired

💼 Enterprise features:

  • • Pokročilé týmové statistiky
  • • API integrace
  • • Custom dashboards
  • • Priority support

✅ Zdarma pro vývojáře • ✅ Production-ready • ✅ Real-world example

Next.jsFirebaseTypeScriptarchitekturaSaaS