Architecture Overview
CakeMarket v2.0 is built on a modern full-stack TypeScript architecture.
Technology Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 14+ (App Router) |
| Language | TypeScript |
| Database | SQLite via Drizzle ORM (better-sqlite3) |
| Authentication | Auth.js v5 with JWT strategy |
| Styling | Tailwind CSS |
| Validation | Zod |
| Payments | Stripe Connect |
| IDs | UUID v4 for all primary keys |
| i18n | JSON-based translations (English + Polish) |
| PWA | Service worker + manifest.json |
App Router Structure
The application uses Next.js route groups to separate concerns:
src/app/
├── (buyer)/ # Buyer-facing pages
├── (seller)/ # Seller portal
│ └── seller/
│ ├── dashboard/
│ ├── requests/
│ ├── orders/
│ ├── organisation/
│ ├── profile/
│ ├── payments/
│ ├── onboarding/
│ └── pending/
├── (admin)/ # Admin panel
│ └── admin/
│ ├── disputes/ # Legacy compatibility redirect; no active dispute UI
│ └── orders/
├── about/ # Marketing pages
└── api/ # API routes
├── admin/
├── guest/
├── images/
├── messages/
├── offers/
├── orders/
├── organisations/
├── payments/
├── profile/
├── requests/
├── reviews/
└── seller/
Key Architectural Patterns
Data Access Layer (DAL)
All database queries go through src/lib/dal/. Components and API routes never import Drizzle directly — they use DAL functions instead.
src/lib/dal/
├── organisations.ts
├── orders.ts
├── offers.ts
├── requests.ts
├── users.ts
├── payments.ts
├── messages.ts
├── reviews.ts
└── ...
Auth Helpers
Centralised authentication functions in src/lib/auth/helpers.ts:
getCurrentUser()— Get the current session userrequireAuth()— Require authentication (returns user or throws)requireAdmin()— Require admin rolerequireOrgMember()— Require membership in a specific organisationrequireOrgAdmin()— Require admin role within an organisation
Guest Sessions
Guest users are supported via localStorage tokens and browser fingerprinting. Guests can:
- Create cake requests
- Receive and accept offers
- Place orders
Guest sessions can be converted to full accounts via POST /api/guest/convert, which migrates requests, orders, and preferences.
Price Handling
All prices are stored as integers in grosze (1/100 PLN) to avoid floating-point issues:
formatPrice()— Converts grosze to display formatcalculateDeposit()— Computes deposit from totalcalculateCommission()— Computes platform feecalculateBalance()— Computes remaining balance
Business Constants
Defined in src/lib/utils/constants.ts:
| Constant | Value |
|---|---|
| Commission rate | 10% |
| Deposit range | 20–100% |
| Request expiry | 24 hours |
| Smart sort: rating weight | 40% |
| Smart sort: price weight | 35% |
| Smart sort: certification weight | 25% |
Component Architecture
src/components/
├── ui/ # Reusable UI primitives (buttons, modals, badges, etc.)
├── shared/ # Shared components (FAQ accordion, etc.)
├── buyer/ # Buyer-specific components
└── seller/ # Seller-specific components
├── OfferForm.tsx
├── RequestCard.tsx
├── RequestDetailModal.tsx
├── OrderCard.tsx
├── OrderDetail.tsx
├── MessageThread.tsx
├── PortfolioManager.tsx
├── MembersList.tsx
├── CertificationManager.tsx
├── SidebarNav.tsx
├── BottomTabs.tsx
└── TopBar.tsx
Validation
All input validation uses Zod schemas defined in src/lib/validation/schemas.ts. Schemas are shared between client-side forms and API route handlers.
Email Notifications
24 HTML email templates (Polish + English) in src/lib/notifications/email.ts:
- Production: Sent via Resend
- Development: Logged to console
Helper functions include sendOfferReceivedEmail, sendBalancePaymentDueEmail, sendSellerMarkedDeliveredEmail, sendReviewPromptEmail, and sendNewOrganisationAdminEmail.