Skip to main content

Saheb App Design System

Screenshot 2026-01-01 at 14.04.11.png

1. Purpose of This Documentation

This presentation explains the design and technical decisions behind our application features.


a. We will be covering:


1. Technology Stack

  • Backend framework

  • Web frontend framework

  • Mobile frontend framework

  • Database technology (PostgreSQL, MySQL, etc.)

  • UI libraries

  • Supporting libraries and tools (state management, datafetching, utilities)


2. Application Architecture & Project Structure

  • Backend folder structure

  • Frontend folder structure

  • Design patterns and development strategies


3. Localization & Language Support

  • Multi-language database translation strategy

  • RTL (Right-to-Left) support on web and mobile

  • Hijri calendar localization


4. Core Domain Logic

  • Qibla calculation logic

  • Prayer time calculation methods

  • Hijri time caulculation methods


5. Media Management

  • Audio resource handling (Sermon / موعظة)

  • Video hosting and streaming strategy


6. Notifications & Communication

  • Timezone-aware push notification system

  • Email notification architecture

  • Notification reliability and crash handling

  • Scheduling notificaiton with cron


8. Application Standards

  • Request logging

  • User event and activity tracking

  • Error and crash reporting

  • API versioning


b. How Each Section Is Documented

For every section in this documentation, we consistently answer the following questions:

  • What solution did we choose?

  • Why did we choose this solution?

  • What are the pros (optional) and cons of this solution?

  • What alternative approaches were considered?


1. Technology Stack

a. Backend Framework


What solution did we choose?
We chose Nodejs + Expressjs + NestJS as the backend framework.


Why did we choose this solution?

  • Team is more experienced with
  • Widely used on our code base
  • Built with TypeScript by default


Pros

  • Clear module-based architecture

  • Strong TypeScript support

  • Easy integration with databases, authentication, and queues

  • Express Rich community

  • Supports modular development and scalability

  • Fits well for APIs serving web and mobile apps

  • One backend server for both mobile and web


Cons

  • Slightly more boilerplate

  • Integration Complexity with Some Legacy Libraries


Alternative Approaches

  • Express.js

  • Fastify

  • Laravel (PHP)

  • Spring Boot (Java)


b. Web Frontend Framework


What solution did we choose?
We chose Next.js using the App Directory.


Why did we choose this solution?

  • Built-in support for modern React features

  • File-based routing with the App Directory

  • Good integration with backend APIs

  • Built-in layouts, and loading states, server errors handlings

Pros

  • Clear and scalable project structure

  • Server-side rendering and static generation

  • Strong support for SEO and web performance

  • Server Components


Cons

  • Some ecosystem libraries are still adapting

  • Build & Deployment Complexity

  • Rapidly Changing Features


Alternative Approaches

  • React ( Vite )

  • React (Tanstack start )

  • Nuxt.js

  • Angular

c. Mobile Frontend Framework


What solution did we choose?
We chose React Native with Expo.


Why did we choose this solution?

  • Allows building iOS Android apps  and web from a single codebase

  • Quick testing on simulators, emulators, and real devices
  • Built-in support for push notifications, sensors, and device APIs

  • Large community and ecosystem

  • Deployment built-in (EAS Hosting)
  • Routing and Authentification built in
  • Assets management
  • AI intergration (expo mcp server)

Cons

  • Some native modules require custom development (Ejecting from Expo)


Alternative Approaches

  • Pure React Native (without Expo)

  • Flutter

  • Native iOS (Swift) / Android (Kotlin) development

d. Database Technology


What solution did we choose?
We chose PostgreSQL as the database for our application.


Why did we choose this solution?

  • Team is more experienced with
  • Good ecosystem and tooling support
  • Reliable and stable relational database

  • Widely used in the industry with large community and resources

  • Works well with TypeORM for Nestjs integration


Pros

  • Good ecosystem and tooling support

  • Open-source and actively maintained

  • Supports advanced features like JSON, full-text search, and indexes

  • Strong support for transactions


Cons

  • Can be more complex to set up than simpler databases (like SQLite)

  • Overkill for very small projects

e. UI Libraries


1. Web Frontend component library

What solution did we choose?
We chose shadcn/ui integrated with Tailwind CSS, and Figma designs for reference.


Why did we choose this solution?

  • Tailwind provide CSS utility classes to speed up development, so instead of writing custom CSS files, we style components directly using small, reusable classes
    • Without Tailwind (Traditional CSS):
      • <button class="btn">
          Save
        </button>
      • .btn {
          background-color: blue;
          color: white;
          padding: 8px 16px;
          border-radius: 6px;
        }
    • With Tailwind:
      • <button class="bg-blue-600 text-white px-4 py-2 rounded-md">
          Save
        </button>
        
  • Provides a ready-to-use component library built on Tailwind CSS

  • Tailwind
allows

image.png

fast

image.png

and consistent styling across the app
  • Open Code: The top layer of your component code is open for modification
    • import * as React from "react"
      import { Slot } from "@radix-ui/react-slot"
      import { cva, type VariantProps } from "class-variance-authority"
      
      import { cn } from "@/lib/utils"
      
      const buttonVariants = cva(
        "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
        {
          variants: {
            variant: {
              default: "bg-primary text-primary-foreground hover:bg-primary/90",
              destructive:
                "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
              outline:
                "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
              secondary:
                "bg-secondary text-secondary-foreground hover:bg-secondary/80",
              ghost:
                "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
              link: "text-primary underline-offset-4 hover:underline",
            },
            size: {
              default: "h-9 px-4 py-2 has-[>svg]:px-3",
              sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
              lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
              icon: "size-9",
              "icon-sm": "size-8",
              "icon-lg": "size-10",
            },
          },
          defaultVariants: {
            variant: "default",
            size: "default",
          },
        }
      )
      
      function Button({
        className,
        variant = "default",
        size = "default",
        asChild = false,
        ...props
      }: React.ComponentProps<"button"> &
        VariantProps<typeof buttonVariants> & {
          asChild?: boolean
        }) {
        const Comp = asChild ? Slot : "button"
      
        return (
          <Comp
            data-slot="button"
            data-variant={variant}
            data-size={size}
            className={cn(buttonVariants({ variant, size, className }))}
            {...props}
          />
        )
      }
      
      export { Button, buttonVariants }
  • Distribution: A flat-file schema and command-line tool make it easy to distribute components.
    • npx shadcn@latest add button
  • AI-Ready: Open code for LLMs to read, understand, and improve (mcp server).
    • {
        "registries": {
          "@acme": "https://acme.com/r/{name}.json"
        }
      }
  • Figma designs give a visual reference for UI consistency

  • Huge ecosystem built around shadcn.
    1. shadcn/studio
    2. shadcn text editor
    3. react-dnd-kit-tailwind-shadcn-ui
    4. shadcn-chat
  • Fits well with React and Next.js

  • Very customizable :
    1. you can change the whole app theme by changing only globall.css file :
    2. change component source code.

Pros

  • Lightweight and modern styling approach


Cons

  • Learning curve for Tailwind if new to utility-first CSS


Alternative Approaches

  • Material-UI (MUI)

  • Ant Design

  • Chakra UI

  • Custom components from scratch

image.png

image.png