Overview
Macondo Link Manager is built with a domain-driven architecture where the backend serves as the single source of truth and the frontend acts as a declarative consumer. This design ensures consistency, predictability, and scalability. The project is organized as a monorepo containing:- Backend (API): Node.js + Fastify + Prisma
- Frontend (Web): Next.js 14 with App Router
Domain Model
The system follows a hierarchical domain structure:Entities
Client
Client
Represents an agency client. Clients own campaigns and links.Relationships:
- Has many
Campaigns (one-to-many) - Has many
Links (one-to-many)
- Deleting a client deletes all associated campaigns and links
Campaign
Campaign
Represents a marketing campaign associated with a client.Relationships:
- Belongs to one
Client(many-to-one) - Has many
Links (one-to-many)
- Deleting a campaign sets
campaignIdto NULL on associated links (SetNull)
Link
Link
The core entity representing a shortened link.Properties:
originalUrl: The long URL to redirect toshortCode: Unique identifier for the shortened linkuserId: Creator of the linkclientId: Associated clientcampaignId: Associated campaign (optional)
- Belongs to one
User - Belongs to one
Client - Belongs to one
Campaign(optional) - Has many
Tags (many-to-many via LinkTag) - Has many
Clicks (one-to-many)
Links are global to the organization, not owned exclusively by campaigns. A link can exist without a campaign.
Tag
Tag
Categorization labels that can be applied to links.Relationships:
- Has many
Links (many-to-many via LinkTag)
- Tags are sent as
string[](array of names) - Non-existent tags are created automatically
- Updates replace the entire tag set transactionally
- No duplicate tags or orphaned relationships
Click
Click
Records each click event on a shortened link.Properties:
timestamp: When the click occurredipAddress: IP address of the visitoruserAgent: Browser/device informationcountry: Determined via GeoIPcity: Determined via GeoIPisBot: Whether the click is from a botbotReason: Explanation if detected as bot
Database Schema
The platform uses PostgreSQL with Prisma ORM. Below is the complete schema:Key Schema Features
- UUIDs for all primary keys
- Cascade deletes for maintaining referential integrity
- Indexes on foreign keys and timestamp columns for query performance
- Text fields for potentially long content (URLs, user agents)
- Optional fields marked with
?for flexible data capture
Technology Stack
Backend (API)
Fastify
High-performance web framework with schema validation and plugin architecture
Prisma
Type-safe ORM for PostgreSQL with migrations and query builder
Zod
Runtime schema validation for API inputs and outputs
JWT
Secure authentication with JSON Web Tokens stored in httpOnly cookies
Frontend (Web)
Next.js 14
React framework with App Router for server components and routing
TanStack Query
Powerful data fetching, caching, and state management
shadcn/ui
Beautifully designed components built with Radix UI and Tailwind
React Hook Form
Performant forms with Zod validation
Infrastructure
- Vercel: Frontend hosting with edge network and automatic deployments
- Railway: Backend API and PostgreSQL database hosting
- Docker: Containerization for local development and deployment
- Custom Domains: Production URLs at
mcd.ppg.br
Architectural Principles
The system follows these core principles:Backend as Single Source of Truth
Backend as Single Source of Truth
- All business logic resides in the backend
- Frontend never performs domain calculations
- API returns complete, ready-to-render data models
- State management is simplified on the client
Persistent State Over Heuristics
Persistent State Over Heuristics
- Metrics use persisted state (e.g.,
isBotfield) - No runtime calculations or heuristics for critical data
- Aggregations are computed by the backend
- Historical data remains consistent over time
Transactional Operations
Transactional Operations
- Critical operations use database transactions
- Tag updates replace the entire set atomically
- No partial states or race conditions
- Cascade deletes maintain referential integrity
Global Link Ownership
Global Link Ownership
- Links are global to the organization
- Links can exist without campaigns
- Metrics aggregate across the entire organization
- Flexible association with clients and campaigns
Symmetric Create and Update
Symmetric Create and Update
- Create and Update operations follow the same contract
- Same validation rules apply to both
- Predictable API behavior
- Easier client implementation
Backend Responsibilities
The backend handles:-
Authentication & Authorization
- Google OAuth integration
- Domain-based access control
- JWT token generation and validation
-
Data Aggregation
- Count campaigns per client
- Count links per client and campaign
- Calculate click metrics (by date, browser, country, city)
- Filter bot traffic
-
Business Logic
- URL shortening and validation
- Tag management (create, associate, sync)
- QR code generation
- Bot detection at write-time
-
Data Persistence
- CRUD operations for all entities
- Transaction management
- Database migrations
- Referential integrity
Frontend Responsibilities
The frontend handles:-
User Interface
- Responsive design with Tailwind CSS
- Component composition with shadcn/ui
- Dark/light theme support
-
Data Fetching & Caching
- TanStack Query for server state
- Optimistic updates
- Cache invalidation
- Loading and error states
-
Form Management
- React Hook Form for performance
- Zod schema validation
- Client-side validation feedback
-
Routing & Navigation
- Next.js App Router
- Dynamic routes for clients, campaigns, links
- Server components where applicable
The frontend never performs domain calculations like counting, filtering, or aggregating. It only displays data provided by the backend.
Data Flow
Here’s how data flows through the system:Link Creation Flow
- User submits form in frontend
- Frontend validates with Zod schema
- POST request sent to
/api/links - Backend validates with Zod
- Backend creates link record
- Backend associates tags (creates new ones if needed)
- Backend returns complete link object with tags
- Frontend updates cache and displays success
Click Tracking Flow
- User visits shortened link (
https://li.mcd.ppg.br/{shortCode}) - Backend receives redirect request
- Backend extracts IP, user agent
- Backend performs bot detection
- Backend determines country/city via GeoIP
- Backend creates click record with
isBotflag - Backend redirects to original URL
- Metrics queries only count clicks where
isBot = false
Dashboard Metrics Flow
- Frontend requests dashboard data
- Backend queries database with joins and aggregations
- Backend filters out bot clicks
- Backend groups by date, browser, country, city
- Backend returns structured metrics
- Frontend renders charts using Recharts
- TanStack Query caches result
Authentication Flow
JWTs are stored in httpOnly cookies to prevent XSS attacks. The frontend never accesses the token directly.
Read Models
The backend provides optimized read models for list views: Client List:Scalability Considerations
Database
- Indexed foreign keys for fast joins
- Indexed timestamps for time-based queries
- Partitioning strategy for clicks table (future)
- Connection pooling via Prisma
API
- Fastify’s high-performance event loop
- Stateless design for horizontal scaling
- Swagger documentation for API discovery
- Health check endpoint for monitoring
Frontend
- Static generation where possible
- Server components to reduce client JS
- Code splitting via Next.js
- TanStack Query caching to reduce API calls
Monitoring & Health
The API provides a health check endpoint:- Container health checks
- Load balancer probes
- Uptime monitoring
- CI/CD pipeline validation
Next Steps
API Reference
Explore all available endpoints
Core Features
Learn about link management and features
Database Guide
Manage schema changes with Prisma
Deployment
Deploy to production environments
