Architectural Decisions
This document provides an overview of key architectural decisions made in this monorepo. Detailed decisions are documented as Architectural Decision Records (ADRs) in /docs/05-adr/.
Decision Index
Phase 1: Foundation (Infrastructure & Duplication Reduction)
- ADR-001: Frontend Foundation Phase
- Decision: Introduce shared GraphQL client and generic authentication system
- Status: ✅ Implemented
- Impact: Reduced code duplication, established foundation for future phases
Quick Reference
Monorepo Structure
Decision: Keep all code in a single repository rather than multiple repos.
Why:
- Code sharing between products is easier
- Consistent patterns across applications
- Single deployment pipeline
- Shared dependencies
Trade-offs:
- ✅ Easier code reuse
- ✅ Single source of truth
- ⚠️ Larger repository
- ⚠️ Need clear boundaries between apps
Next.js Dual Router Strategy
Decision: Use both Pages Router (legacy) and App Router (modern) simultaneously.
Why:
- Personal website already uses Pages Router
- App Router provides better DX for new apps
- Gradual migration path
Trade-offs:
- ✅ No breaking changes to existing site
- ✅ New apps benefit from App Router features
- ⚠️ Two routing paradigms to maintain
- ⚠️ Migration needed eventually
Shared GraphQL Client
Decision: Create custom, lightweight GraphQL client instead of Apollo/Relay.
Why:
- No heavy framework overhead
- Full control over request/response handling
- Simple use cases don’t need complex features
- Smaller bundle size
Trade-offs:
- ✅ Lightweight and fast
- ✅ No external dependencies
- ⚠️ Manual query construction
- ⚠️ No built-in caching (handled by React Query where needed)
Reference: ADR-001
Generic Authentication System
Decision: Create generic AuthProvider that can be configured per domain.
Why:
- Reduce duplication across Discart-me, QRACK, BoxHub
- Consistent auth patterns
- Easier to add new apps
Trade-offs:
- ✅ Code reuse
- ✅ Consistent behavior
- ⚠️ Abstraction complexity
- ⚠️ Migration effort for existing apps
Reference: ADR-001
UI Component Library
Decision: Create minimal UI base (/components/ui) with generic, reusable components.
Why:
- Consistent design system
- Reduce duplication
- Safe for use across all apps
- Foundation for design system
Trade-offs:
- ✅ Reusable components
- ✅ Consistent styling
- ⚠️ Need to maintain backward compatibility
- ⚠️ Balance between generic and useful
Reference: UI Base Guidelines
Image Processing Library
Decision: Extract image processing logic into shared library.
Why:
- Duplicate logic in QRACK and Discart-me
- Mobile camera images are too large
- Consistent processing across apps
- Better UX (faster uploads)
Trade-offs:
- ✅ Code reuse
- ✅ Better mobile performance
- ✅ Consistent behavior
- ⚠️ Need to handle different use cases (File vs data URL)
Reference: Image Upload Audit
Feature-Driven Organization
Decision: Organize code by feature in /src/features/ rather than by type.
Why:
- Easier to find related code
- Self-contained modules
- Better for code splitting
- Clearer boundaries
Structure:
features/
auth/
components/
hooks/
types.ts
Trade-offs:
- ✅ Clear boundaries
- ✅ Self-contained features
- ⚠️ Some duplication (similar features across domains)
- ⚠️ Need clear guidelines
TypeScript Path Aliases
Decision: Use path aliases (@/, @/src/) instead of relative imports for shared code.
Why:
- Cleaner imports
- Easier refactoring
- Clear distinction between shared and local code
Trade-offs:
- ✅ Better DX
- ✅ Cleaner imports
- ⚠️ Need TypeScript configuration
- ⚠️ IDE support required
Tailwind CSS Over CSS-in-JS
Decision: Use Tailwind CSS utility classes instead of CSS-in-JS libraries.
Why:
- No runtime CSS generation
- Smaller bundle size (with purging)
- Faster development
- Consistent design tokens
Trade-offs:
- ✅ Better performance
- ✅ Rapid development
- ⚠️ Learning curve
- ⚠️ Long class names (mitigated with components)
React Context Over State Management Libraries
Decision: Use React Context API for global state instead of Redux/Zustand.
Why:
- Simple use cases (auth, theme)
- No external dependencies
- Built-in React feature
- Sufficient for current needs
Trade-offs:
- ✅ Simple and lightweight
- ✅ No dependencies
- ⚠️ May need library for complex state (future)
- ⚠️ No built-in devtools
Future Consideration: React Query for server state (partially implemented)
Decision Process
When to Create an ADR
Create an ADR when:
- Making a significant architectural change
- Choosing between multiple viable solutions
- The decision affects multiple parts of the system
- Future developers need to understand the rationale
See existing ADRs in /docs/05-adr/ for format. Include:
- Context — What problem does this solve?
- Decision — What was chosen?
- Consequences — What are the trade-offs?
- Status — Is this implemented? Superseded?
Future Decisions to Make
Planned
Under Consideration
- Forms Library — React Hook Form if forms become complex
- State Management — Zustand if Context becomes unwieldy
- Monitoring — Error tracking (Sentry) and analytics
- Performance — Bundle analysis and optimization strategy