Stack, domains, layers and code organisation
Architecture
This page describes Xedul's technical architecture. It's aimed at people building on the product or evaluating its foundations, not at end users.
Stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 15 with App Router, React 19, TypeScript 5.8 |
| Styling | Tailwind CSS with a token-based design system |
| State | React Query for remote data, a light store for interface state |
| Backend | Supabase: PostgreSQL, auth, realtime, storage |
| Authentication | Supabase Auth with role-based access control |
| Realtime | Supabase Realtime subscriptions with caching |
| Internationalisation | `next-intl` with dynamic language switching |
Repository structure
Xedul/ ├── app-web/ Next.js application │ └── src/ │ ├── app/ App Router routes │ ├── domains/ domain logic │ ├── infrastructure/ API, database, realtime, providers │ ├── shared/ reusable components, hooks, utilities │ ├── translations/ translation files │ └── types/ TypeScript definitions ├── integrations/ Gmail and Outlook add-ons, manifests ├── supabase/ configuration and migrations ├── tooling/ lint, repository scripts └── Doc/ internal documentation
Domain architecture
Business logic is organised by domain, not by file type. Each domain holds what its area needs: types, hooks, API calls, components.
The domains present include auth, customers, contacts, projects, tasks, calendar, documents, comments, notifications, reminders, organization, users, statuses, chat, and the two email-integration domains.
The practical consequence: adding a feature to one area almost always touches a single domain folder, instead of spreading across five folders organised by type.
The infrastructure layer
Beneath the domains sits infrastructure, insulating the rest of the application from technical detail:
- API controllers — separate business logic from route handlers.
- Database layer — data access with a repository pattern and typing.
- Provider composition — application state and React context management.
- Realtime system — change synchronisation with caching, to avoid repeating the same requests.
Layered type system
Xedul carries type safety from the database up to the interface, distinguishing layers with different responsibilities:
| Layer | Role |
|---|---|
| Database types | Schema and database operations |
| Domain types | Business logic |
| UI types | Component props and interface state |
| Adapter types | Data transformation and external integrations |
| Branded types | Typed identifiers, so two different IDs can't be confused |
Branded types deserve a note: they prevent, at compile time, passing a client identifier where a case identifier is required. At runtime that mistake would be silent.
Route structure
The App Router organises pages into groups:
(marketing)— public pages.(auth)— sign-in, sign-up, password recovery, email confirmation.(authenticated)— the application proper, behind authentication.api/— server endpoints per domain.gmail/andoutlook/— bridging pages for the email integrations.
Route groups let each area carry its own layout and providers. Marketing pages, for instance, don't load the application's providers: the bundle stays small.
Database schema
The schema is multi-tenant, with the organisation as root entity.
Core entities — organisations, users, customers, projects, contacts, tasks.
Supporting entities — sectors, lead sources, company sizes, project stages, task statuses, documents, metrics.
Every work table carries the organisation reference. That's the foundation of the isolation described in Security.
Realtime
Changes propagate to subscribed clients through Supabase Realtime. The caching layer in front of subscriptions avoids invalidating more than necessary: the aim is that two people on the same case see the same state without reloading, and without generating traffic proportional to the number of open tabs.
Design system
The interface rests on a token system: colours, typography, spacing, radii, shadows and animations are defined centrally and consumed by components.
The defining traits: Inter as the interface typeface, a lot of white and light grey, near-black grey for primary actions, blue as the accent for links and focus states, generous radii on foreground containers and very soft shadows.
The landing page and the documentation you're reading use different typography from the product, to read as communication material, but share the palette, radii and spacing relationships.
Environments and release
The project uses a continuous integration and delivery pipeline with quality gates — lint, type-check, tests, security audit — before every deployment. The development branch publishes to a preview environment; the main branch follows a strategy that validates the deployment before applying schema changes to production.