Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
316
04-ARCHITECTURE-COMPARISON.md
Normal file
316
04-ARCHITECTURE-COMPARISON.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# Port Nimara CRM — Full-Stack Architecture Comparison
|
||||
|
||||
**Compiled:** 2026-03-11
|
||||
**Context:** AI-first development (Claude Code + Codex), self-hosted Docker, real-time updates required, PostgreSQL + Drizzle ORM already decided, website is separate codebase needing public API
|
||||
|
||||
---
|
||||
|
||||
## Constraints (apply to ALL options)
|
||||
|
||||
These are fixed regardless of framework choice:
|
||||
|
||||
| Constraint | Detail |
|
||||
| ------------ | -------------------------------------------------------------------------------- |
|
||||
| Database | PostgreSQL + Drizzle ORM |
|
||||
| Auth | Keycloak OIDC (existing infrastructure) |
|
||||
| File storage | MinIO S3 (existing infrastructure) |
|
||||
| E-signatures | Documenso (existing infrastructure, self-hosted) |
|
||||
| Deployment | Docker containers, self-hosted server, Gitea CI/CD |
|
||||
| Real-time | Live updates required (berth status changes, interest updates, signature events) |
|
||||
| Public API | REST endpoints for website berth map + interest registration |
|
||||
| Development | AI-assisted (Claude Code / Codex writing ~90%+ of code) |
|
||||
| Team size | Solo developer (Matt) + AI tools |
|
||||
|
||||
---
|
||||
|
||||
## Option A: Next.js (React) + tRPC
|
||||
|
||||
### The Stack
|
||||
|
||||
| Layer | Technology |
|
||||
| ---------------- | ------------------------------------------------------------ |
|
||||
| Framework | Next.js 15 (App Router) |
|
||||
| Language | TypeScript (strict) |
|
||||
| UI library | React 19 |
|
||||
| Styling | Tailwind CSS + Maritime design tokens |
|
||||
| Components | shadcn/ui (copy-paste, Radix primitives, fully customizable) |
|
||||
| Internal API | tRPC v11 (type-safe, no API routes to define for CRM pages) |
|
||||
| Public API | Next.js Route Handlers (REST endpoints for website) |
|
||||
| Real-time | tRPC subscriptions over WebSocket (via `ws` or `uWebSocket`) |
|
||||
| State | TanStack Query (server state) + Zustand (UI state) |
|
||||
| ORM | Drizzle ORM |
|
||||
| Jobs | BullMQ + Redis |
|
||||
| Containerization | Docker (node:20-alpine) + nginx reverse proxy |
|
||||
|
||||
### How it handles your CRM use cases
|
||||
|
||||
**Interest CRUD**: tRPC procedure `interests.create` / `interests.update` / `interests.list` — fully type-safe from database schema to UI component. Change a field in Drizzle schema, TypeScript immediately flags every component that references it.
|
||||
|
||||
**Real-time berth status**: tRPC subscription `berths.onStatusChange` — when a berth is linked to an interest and auto-moves to "Under Offer", all connected clients receive the update via WebSocket. No polling needed.
|
||||
|
||||
**EOI/Documenso workflow**: Server-side tRPC procedures handle the full lifecycle. Webhook endpoint is a standard Next.js Route Handler (REST, since Documenso calls it externally).
|
||||
|
||||
**Public berth map API**: Standard Next.js Route Handlers — `GET /api/public/berths` and `POST /api/public/interests`. These are separate from tRPC and serve the website.
|
||||
|
||||
**Spec sheet import (AI-assisted)**: Upload endpoint receives PDF/Excel, server-side processing with SheetJS (Excel) or PDF extraction, AI API call to interpret/map columns, preview diff shown to admin, confirmation triggers bulk upsert via Drizzle.
|
||||
|
||||
**Docker deployment**: Official self-hosting guide. Standalone output mode produces a minimal Node.js server. Requires nginx reverse proxy for production (handles TLS, rate limiting, slow connections). Redis sidecar for BullMQ + session cache.
|
||||
|
||||
### Assessment
|
||||
|
||||
| Dimension | Rating | Notes |
|
||||
| ----------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AI code generation quality | **Excellent** | Largest training corpus. Claude Code is most fluent with React/Next.js. shadcn/ui has massive example coverage. |
|
||||
| Component ecosystem | **Excellent** | Deepest of any framework. Every library has a React-first version. TanStack Table, React Hook Form, Recharts, etc. |
|
||||
| tRPC integration | **Excellent** | First-class support. Most tRPC examples are Next.js. |
|
||||
| Real-time (WebSocket) | **Good** | Works but requires separate WebSocket server alongside Next.js (Vercel doesn't support WS, but irrelevant for self-hosting). Needs `next-ws` or custom server. |
|
||||
| Self-hosted Docker | **Good** | Works well with standalone output mode. Requires nginx reverse proxy. More configuration than Nuxt/SvelteKit. |
|
||||
| Bundle size / performance | **Adequate** | React runtime is larger than alternatives. For an internal CRM used by a small team, this is irrelevant. |
|
||||
| Learning curve (reading code) | **Moderate** | JSX, hooks, useEffect patterns. React has more "gotchas" than Vue/Svelte but AI handles them well. |
|
||||
| Long-term maintenance | **Excellent** | Backed by Vercel. Massive community. Won't disappear. |
|
||||
|
||||
**Pros:**
|
||||
|
||||
- AI tools generate the most reliable code in this ecosystem
|
||||
- Largest component library ecosystem by far
|
||||
- tRPC + Drizzle + Next.js is an extremely well-documented combination
|
||||
- shadcn/ui gives you beautiful, accessible components you fully own and can restyle to Maritime
|
||||
- TanStack Query handles caching, optimistic updates, background refresh out of the box
|
||||
- Most Stack Overflow answers, most GitHub examples, most blog posts
|
||||
|
||||
**Cons:**
|
||||
|
||||
- React's mental model (hooks, closures, re-renders) can be confusing when reading/debugging
|
||||
- WebSocket support needs custom server setup (not complex, but not built-in)
|
||||
- Next.js is optimized for Vercel — self-hosting works but you miss some platform features
|
||||
- Heavier runtime than Svelte (irrelevant for small team internal tool)
|
||||
- App Router patterns still evolving — some tutorials use old Pages Router
|
||||
|
||||
---
|
||||
|
||||
## Option B: Nuxt 3 (Vue) + tRPC
|
||||
|
||||
### The Stack
|
||||
|
||||
| Layer | Technology |
|
||||
| ---------------- | ------------------------------------------------------------------- |
|
||||
| Framework | Nuxt 3 (latest) |
|
||||
| Language | TypeScript (strict) |
|
||||
| UI library | Vue 3 (Composition API) |
|
||||
| Styling | Tailwind CSS + Maritime design tokens |
|
||||
| Components | Nuxt UI v3 (Radix Vue based, Tailwind-native) OR Radix Vue + custom |
|
||||
| Internal API | trpc-nuxt (community adapter) |
|
||||
| Public API | Nitro server routes (REST endpoints for website) |
|
||||
| Real-time | Nitro WebSocket (experimental) or Socket.io alongside Nitro |
|
||||
| State | VueQuery / TanStack Query Vue (server state) + Pinia (UI state) |
|
||||
| ORM | Drizzle ORM |
|
||||
| Jobs | BullMQ + Redis |
|
||||
| Containerization | Docker (node:20-alpine), Nitro handles both API + SPA |
|
||||
|
||||
### How it handles your CRM use cases
|
||||
|
||||
**Interest CRUD**: trpc-nuxt procedures with same type-safety pattern as Next.js. Vue's Composition API (`ref`, `computed`, `watch`) is arguably more intuitive than React hooks for someone reading code.
|
||||
|
||||
**Real-time berth status**: Nitro's experimental WebSocket support (CrossWasm v1 in Nitro v3). Less mature than the Next.js WebSocket ecosystem. Alternatively, run Socket.io alongside Nitro — works but adds complexity.
|
||||
|
||||
**EOI/Documenso workflow**: Nitro server routes handle webhooks. Same pattern as current system (Nitro is what you already have).
|
||||
|
||||
**Public berth map API**: Nitro server routes — cleaner than Next.js Route Handlers. Nitro is genuinely good at this.
|
||||
|
||||
**Spec sheet import**: Same approach as Next.js — server-side processing with AI interpretation.
|
||||
|
||||
**Docker deployment**: Nitro's output is a self-contained Node.js server. No reverse proxy strictly required (though recommended). Simpler Docker setup than Next.js.
|
||||
|
||||
### Assessment
|
||||
|
||||
| Dimension | Rating | Notes |
|
||||
| ----------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| AI code generation quality | **Good** | Well-represented in training data. Slightly fewer examples than React. Claude Code handles Vue/Nuxt competently. |
|
||||
| Component ecosystem | **Good** | Nuxt UI v3 is excellent and built specifically for Nuxt. Fewer third-party options than React. |
|
||||
| tRPC integration | **Adequate** | Community adapter (trpc-nuxt). Works but fewer examples, less battle-tested than Next.js tRPC. |
|
||||
| Real-time (WebSocket) | **Adequate** | Nitro WebSocket is experimental. Works with adapter-node but less mature. Socket.io fallback is proven. |
|
||||
| Self-hosted Docker | **Excellent** | Nitro produces clean standalone output. Simplest Docker setup of the three. |
|
||||
| Bundle size / performance | **Good** | Smaller than React, larger than Svelte. |
|
||||
| Learning curve (reading code) | **Easy** | Vue's template syntax is the most readable. `<script setup>` is clean. Closest to plain HTML. |
|
||||
| Long-term maintenance | **Good** | Backed by Nuxt Labs. Strong community. Stable but smaller than React ecosystem. |
|
||||
|
||||
**Pros:**
|
||||
|
||||
- You already have familiarity with the patterns from the current codebase
|
||||
- Vue's template syntax is the most human-readable (easiest to understand when reviewing AI output)
|
||||
- Nitro server is genuinely excellent for API routes and deployment flexibility
|
||||
- Nuxt UI v3 is tightly integrated and well-maintained by the Nuxt team
|
||||
- Simplest Docker deployment (Nitro just works)
|
||||
- Pinia + VueQuery is a clean state management story
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Smaller AI training corpus than React — more edge cases where Claude Code may hallucinate APIs
|
||||
- tRPC support is community-maintained, not official
|
||||
- WebSocket/real-time support is less mature than alternatives
|
||||
- Fewer third-party component libraries to choose from
|
||||
- Risk of repeating patterns from the current codebase rather than improving them
|
||||
|
||||
---
|
||||
|
||||
## Option C: SvelteKit + tRPC
|
||||
|
||||
### The Stack
|
||||
|
||||
| Layer | Technology |
|
||||
| ---------------- | --------------------------------------------------------------- |
|
||||
| Framework | SvelteKit 2 |
|
||||
| Language | TypeScript (strict) |
|
||||
| UI library | Svelte 5 (runes) |
|
||||
| Styling | Tailwind CSS + Maritime design tokens |
|
||||
| Components | Skeleton UI or shadcn-svelte (Bits UI based) |
|
||||
| Internal API | trpc-sveltekit (community adapter) |
|
||||
| Public API | SvelteKit server routes (REST endpoints for website) |
|
||||
| Real-time | trpc-sveltekit experimental WS or Socket.io sidecar |
|
||||
| State | TanStack Query Svelte (server state) + Svelte stores (UI state) |
|
||||
| ORM | Drizzle ORM |
|
||||
| Jobs | BullMQ + Redis |
|
||||
| Containerization | Docker (node:20-alpine) via adapter-node |
|
||||
|
||||
### How it handles your CRM use cases
|
||||
|
||||
**Interest CRUD**: trpc-sveltekit procedures. Svelte's reactivity model is the simplest of the three — `$state` rune replaces React's `useState`, Vue's `ref`. Less boilerplate.
|
||||
|
||||
**Real-time berth status**: Experimental WebSocket via trpc-sveltekit (only works with adapter-node). Less mature than both Next.js and Nuxt options.
|
||||
|
||||
**EOI/Documenso workflow**: SvelteKit server routes. Clean pattern but fewer examples specific to webhook handling.
|
||||
|
||||
**Public berth map API**: SvelteKit server routes. Works well, similar to Nuxt's Nitro routes.
|
||||
|
||||
**Spec sheet import**: Same approach as others. Fewer AI-processing library examples in Svelte ecosystem.
|
||||
|
||||
**Docker deployment**: adapter-node produces a clean, lightweight Node server. Smallest footprint of the three.
|
||||
|
||||
### Assessment
|
||||
|
||||
| Dimension | Rating | Notes |
|
||||
| ----------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AI code generation quality | **Adequate** | Smallest training corpus. Svelte 5 runes are new — AI tools may generate Svelte 4 patterns. More manual correction needed. |
|
||||
| Component ecosystem | **Limited** | Skeleton UI and shadcn-svelte exist but are less mature. Many libraries are React/Vue only. |
|
||||
| tRPC integration | **Adequate** | Community adapter. WebSocket support explicitly marked "experimental". |
|
||||
| Real-time (WebSocket) | **Limited** | SvelteKit doesn't natively support WebSockets. Experimental via adapter-node only. Most constrained option. |
|
||||
| Self-hosted Docker | **Excellent** | Smallest container image. Lowest resource usage. Cleanest adapter-node output. |
|
||||
| Bundle size / performance | **Excellent** | 40-60% smaller bundles than React. Fastest runtime. Lowest memory usage. |
|
||||
| Learning curve (reading code) | **Easy** | Most intuitive syntax. Closest to vanilla HTML/JS. Svelte 5 runes are clean. |
|
||||
| Long-term maintenance | **Good** | Strong community, Vercel backing. Smaller ecosystem means fewer resources if you hit edge cases. |
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Smallest bundle size and lowest resource usage (best for self-hosting efficiency)
|
||||
- Most intuitive syntax — easiest to read and understand as a non-programmer
|
||||
- Svelte 5 runes are the cleanest reactivity model available
|
||||
- Lightest Docker footprint
|
||||
- Developer experience is genuinely the best of the three for humans
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Smallest AI training corpus — Claude Code and Codex will produce less reliable code, especially for complex patterns
|
||||
- Svelte 5 is recent — AI tools may mix up Svelte 4 and Svelte 5 syntax
|
||||
- Weakest real-time/WebSocket story — experimental only
|
||||
- Smallest component ecosystem — you'll build more from scratch
|
||||
- Fewer Stack Overflow answers and GitHub examples to draw from when stuck
|
||||
- Some React-only libraries have no Svelte equivalent (form builders, advanced table components, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Option D: Next.js (React) + REST (no tRPC)
|
||||
|
||||
Same as Option A but replacing tRPC with traditional REST API routes. Included because tRPC adds complexity and you may prefer a simpler architecture.
|
||||
|
||||
### Differences from Option A
|
||||
|
||||
| Aspect | tRPC (Option A) | REST (Option D) |
|
||||
| ---------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| API definition | Procedures on server, auto-typed on client | Route Handlers on server, manual fetch + types on client |
|
||||
| Type safety | Automatic end-to-end | Manual (Zod schemas shared, but not enforced across boundary) |
|
||||
| Code generation | Less code (no fetch wrappers needed) | More code (API client layer, type definitions) |
|
||||
| Public API for website | Separate REST routes alongside tRPC | Same REST routes serve both CRM and website |
|
||||
| Real-time | tRPC subscriptions (WebSocket) | Socket.io or Server-Sent Events (separate from API) |
|
||||
| External consumers | tRPC only works with TypeScript clients | REST works with any client (mobile app, website, third-party) |
|
||||
| Complexity | Higher (tRPC + REST coexist for different consumers) | Lower (one pattern for everything) |
|
||||
| Debugging | tRPC calls don't show in browser Network tab as clean REST calls | Standard HTTP requests, easy to inspect |
|
||||
|
||||
### Assessment
|
||||
|
||||
| Dimension | Rating | Notes |
|
||||
| -------------------------- | ------------- | ---------------------------------------------------------------------------------------- |
|
||||
| AI code generation quality | **Excellent** | Even more training data for REST patterns than tRPC. |
|
||||
| Simplicity | **Excellent** | One pattern. No tRPC learning curve. Every endpoint is a standard HTTP route. |
|
||||
| Type safety | **Good** | Zod validation + shared types. Not automatic like tRPC but works well. |
|
||||
| Real-time | **Good** | Socket.io alongside Next.js is well-documented. Or Server-Sent Events for simpler cases. |
|
||||
| External API consumers | **Excellent** | Same API serves CRM, website, and any future mobile app or integration. |
|
||||
| OpenAPI compatibility | **Excellent** | REST routes can auto-generate OpenAPI docs. tRPC cannot. |
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Simplest mental model — everything is HTTP requests
|
||||
- One API pattern instead of tRPC + REST coexisting
|
||||
- Easier to debug (browser Network tab shows clean requests)
|
||||
- Same API serves CRM and website (no separate public routes needed)
|
||||
- If you ever build a mobile app or external integration, the API is ready
|
||||
- More AI training examples for REST than tRPC
|
||||
|
||||
**Cons:**
|
||||
|
||||
- More boilerplate (API client layer, manual type sharing)
|
||||
- No automatic type safety across client-server boundary
|
||||
- Need to manually keep request/response types in sync
|
||||
- Real-time requires a separate Socket.io or SSE layer
|
||||
|
||||
---
|
||||
|
||||
## Head-to-Head Comparison
|
||||
|
||||
| Factor | Next.js + tRPC (A) | Nuxt 3 + tRPC (B) | SvelteKit + tRPC (C) | Next.js + REST (D) |
|
||||
| ----------------------------- | ------------------ | ----------------- | -------------------- | ------------------ |
|
||||
| **AI code gen reliability** | ★★★★★ | ★★★★ | ★★★ | ★★★★★ |
|
||||
| **Component ecosystem** | ★★★★★ | ★★★★ | ★★★ | ★★★★★ |
|
||||
| **Real-time maturity** | ★★★★ | ★★★ | ★★ | ★★★★ |
|
||||
| **Docker self-hosting** | ★★★★ | ★★★★★ | ★★★★★ | ★★★★ |
|
||||
| **Type safety (end-to-end)** | ★★★★★ | ★★★★ | ★★★★ | ★★★ |
|
||||
| **Code readability** | ★★★ | ★★★★ | ★★★★★ | ★★★ |
|
||||
| **Architecture simplicity** | ★★★ | ★★★ | ★★★ | ★★★★★ |
|
||||
| **Bundle size / performance** | ★★★ | ★★★★ | ★★★★★ | ★★★ |
|
||||
| **Public API flexibility** | ★★★ | ★★★★ | ★★★ | ★★★★★ |
|
||||
| **Long-term ecosystem** | ★★★★★ | ★★★★ | ★★★★ | ★★★★★ |
|
||||
|
||||
---
|
||||
|
||||
## Recommendation Matrix
|
||||
|
||||
**If AI code generation reliability is your top priority** → Option A or D (Next.js)
|
||||
|
||||
**If deployment simplicity matters most** → Option B (Nuxt) or C (SvelteKit)
|
||||
|
||||
**If you want the simplest architecture to understand and maintain** → Option D (Next.js + REST)
|
||||
|
||||
**If you want maximum type safety with minimum code** → Option A (Next.js + tRPC)
|
||||
|
||||
**If performance and resource efficiency matter most** → Option C (SvelteKit)
|
||||
|
||||
---
|
||||
|
||||
## Author's Recommendation
|
||||
|
||||
**Option D (Next.js + REST)** is the strongest fit for your specific situation. Here's why:
|
||||
|
||||
1. **AI-first development**: Next.js React has the deepest training corpus. REST has even more examples than tRPC. You're optimizing for AI output quality, and this combination maximizes it.
|
||||
|
||||
2. **Architectural simplicity**: tRPC is elegant but adds a second API paradigm alongside the REST routes you need for the website anyway. With REST-only, every endpoint follows the same pattern — simpler for AI tools to generate consistently and simpler for you to understand.
|
||||
|
||||
3. **Real-time**: Socket.io alongside Next.js is battle-tested and well-documented. Simpler than tRPC subscriptions, which require a custom WebSocket server.
|
||||
|
||||
4. **Public API**: The same REST endpoints serve the CRM frontend, the website berth map, and any future integration. No separate public routes to maintain.
|
||||
|
||||
5. **Future-proofing**: If you ever need a mobile app, an external integration, or OpenAPI documentation, REST is ready. tRPC locks you into TypeScript clients.
|
||||
|
||||
6. **Self-hosting**: Next.js standalone output in Docker behind nginx is well-documented. Not the simplest (Nuxt wins there), but the trade-off for the ecosystem advantages is worth it.
|
||||
|
||||
The one thing you'd give up vs. Option A is automatic end-to-end type safety. But with Zod schemas shared between server and client plus TanStack Query's typed hooks, the gap is small in practice.
|
||||
|
||||
**Runner-up**: Option A (Next.js + tRPC) if you value automatic type safety highly and don't mind two API paradigms coexisting.
|
||||
Reference in New Issue
Block a user