# Design Document ## Architecture Overview Layered architecture (UI → Service → Repository → Data) per `docs/architecture.md`. Next.js 15 App Router with Node.js Runtime only (no Edge Runtime). SQLite via better-sqlite3 accessed exclusively through a custom SQL wrapper. No Prisma. ``` app/ (UI: Route Handlers / Server Components / SSE) ↓ services/ (business logic, permission checks, transactions) ↓ repositories/ (SQL, parameter binding, logical-delete filter) ↓ lib/db/ (SQLite connection, SQL wrapper, Migration) ``` ## Component Design ### 1. SQL Wrapper: `lib/db/sqlite.ts` **Responsibilities**: - SQLite connection management (singleton getDb(), WAL, foreign_keys ON) - query (multi-row), get (single-row), execute (insert/update/delete), transaction, close - Error handling **Implementation highlights**: - dbPath = process.env.SQLITE_PATH ?? "./data/app.db" - SqlParams = Record | unknown[] - All repositories use this wrapper; direct better-sqlite3 access forbidden ### 2. Migrator: `lib/db/migrator.ts` **Responsibilities**: - Create schema_migrations table - Read .sql files from migrations dir in filename order - Skip already-applied files (tracked in schema_migrations) - Execute each file in a transaction; rollback on failure ### 3. Initial Schema: `lib/db/migrations/001_initial.sql` 16 tables: users, projects, project_members, board_threads, board_comments, chat_messages, todo_columns, todo_items, file_assets, project_notes, milestones, calendar_events, meetings, meeting_members, notifications, activity_logs + indexes. ### 4. Entity Types: `lib/types/` PascalCase type files for each entity + union types (UserRole, UserStatus, ProjectStatus, ProjectMemberRole, etc.). ### 5. UserRepository findById, findByEmail, create (with passwordHash), update (name/avatarUrl/role/status). ### 6. AuthService register (hash password, create user), login (verify hash, issue session), logout, getCurrentUser, updateProfile. Uses bcrypt. ### 7. Session: `lib/auth/session.ts`, `lib/auth/getCurrentUser.ts` Cookie-based session. getCurrentUser resolves the user from the request cookie. ### 8. ProjectRepository / ProjectMemberRepository Project: findById, findByOwner, findByUser (via join), create, update, delete. ProjectMember: findByProject, findByUser, add, remove, isMember, getRole. ### 9. ProjectService createProject (owner becomes admin member), updateProject, addMember (permission check + notification hook later), removeMember, archiveProject, getDashboard skeleton. ## Data Flow ### Login 1. POST /api/auth/login → AuthService.login(email, password) 2. AuthService → UserRepository.findByEmail → verify bcrypt hash 3. Issue session cookie → return user ### Project creation 1. POST /api/projects → ProjectService.createProject(actorId, input) 2. ProjectService → permission (any authenticated user) → ProjectRepository.create 3. ProjectMemberRepository.add(projectId, ownerId, 'admin') 4. Return project ## Error Handling Strategy ### Custom Error Classes - ValidationError (400) - field, value - ForbiddenError (403) - NotFoundError (404) - resource, id - (409 for unique constraint - handled via SQLite error code) ### Error Handling Patterns Route Handlers catch errors and map to HTTP status. Expected errors use custom classes; unexpected errors propagate + log. ## Test Strategy ### Unit Tests (Vitest) - lib/db/sqlite.test.ts (query/get/execute/transaction, WAL, foreign keys) - lib/db/migrator.test.ts (order, skip applied, rollback) - repositories/UserRepository.test.ts - services/AuthService.test.ts - repositories/ProjectRepository.test.ts - repositories/ProjectMemberRepository.test.ts - services/ProjectService.test.ts Tests use a temp SQLite file (in-memory or tmpdir) to avoid touching real data/. ### Integration Tests (Vitest) - project-member-permission.test.ts (non-member cannot access project data) ### E2E Tests (Playwright) - auth.spec.ts (login, logout, protected screen) - project-management.spec.ts (create, edit, member add/remove, archive) ## Dependent Libraries ```json { "dependencies": { "next": "15", "react": "19", "react-dom": "19", "better-sqlite3": "^11", "bcrypt": "^5", "react-markdown": "^9", "remark-gfm": "^4", "rehype-sanitize": "^6" }, "devDependencies": { "tailwindcss": "^3", "@types/better-sqlite3": "^7", "@types/bcrypt": "^5", "@playwright/test": "^1", "tsx": "^4" } } ``` ## Directory Structure ``` app/ api/auth/{register,login,logout,me}/route.ts api/users/me/route.ts api/projects/route.ts api/projects/[projectId]/route.ts api/projects/[projectId]/members/route.ts api/projects/[projectId]/members/[userId]/route.ts api/admin/migrations/route.ts login/page.tsx profile/page.tsx dashboard/page.tsx projects/[projectId]/{page,members,settings}.tsx layout.tsx, globals.css lib/ db/{sqlite.ts, migrator.ts, run-migrations.ts, migrations/001_initial.sql} auth/{session.ts, getCurrentUser.ts} types/*.ts validators/{userValidator.ts, projectValidator.ts} repositories/{UserRepository,ProjectRepository,ProjectMemberRepository}.ts services/{AuthService,ProjectService}.ts components/layout/{Header,Sidebar,ProjectNav}.tsx tests/unit/..., tests/integration/..., tests/e2e/... ``` ## Implementation Order 1. M1 (branch feature/m1-setup): Next.js setup, deps, config, dir structure, remove boilerplate → merge to main 2. M2 (branch feature/m2-db-foundation): SQL wrapper, migrator, schema, types, migration API, unit tests → merge to main 3. M3 (branch feature/m3-auth-user): UserRepository, AuthService, session, validators, auth API, login/profile pages, unit + e2e → merge to main 4. M4 (branch feature/m4-project-member): Project/Member repos, ProjectService, project API, dashboard/members/settings pages, unit + integration + e2e → merge to main ## Security Considerations - Passwords hashed with bcrypt (never plaintext) - All SQL parameter-bound (no string concatenation) - Auth required on all protected routes (except /api/auth/register, /api/auth/login) - Project access requires isMember check - Admin features require role='system_admin' - .env not committed; secrets via environment ## Performance Considerations - SQLite WAL mode for read/write concurrency - Pagination on list endpoints - Singleton DB connection ## Future Extensibility - Repository pattern allows adding tables without changing wrapper - SseEvent type extensible (M8) - NotificationService/ActivityLogService hooks prepared for M5 (ProjectService.addMember will call notification in M5)