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

    Technical decisions

    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 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

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

    • 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

    • Expo provides a managed workflow for faster development

    • Built-in support for push notifications, sensors, and device APIs

    • Large community and ecosystem

    • Easy to test during development and on real devices
    • Quick testing on simulators, emulators, and real devices

    Pros

    • Cross-platform development saves time and resources

    • Expo handles native build processes, reducing setup complexity

    • Easy to integrate with backend APIs (REST / GraphQL)

    • Hot-reloading for fast development

    • Strong developer and community support

    • Deployment built-in

    • Easy to test during development and on real devices

    • Quick testing on simulators, emulators, and real devices


    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?

    • Provides a ready-to-use component library built on Tailwind CSS

    • Tailwind allows fast 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

    • Fast development using prebuilt components

    • Consistent design across web and mobile platforms

    • Easy to extend and customize with Tailwind

    • Direct mapping from Figma to code speeds up implementation

    • 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