Architecture Overview
Deep dive into Portfolio OS system architecture and design patterns
Architecture Overview
Portfolio OS is built using modern web development best practices with a focus on type safety, performance, and developer experience.
System Architecture
Monorepo Architecture
Portfolio OS uses a monorepo structure with Turborepo and PNPM workspaces to manage multiple applications and shared packages.
Directory Structure
Application Layer
Site App (apps/site)
The public-facing portfolio and blog application.
Key Features:
- Server-side rendering (SSR) for SEO
- Blog content from Hashnode
- Projects showcase with case studies
- Contact form with email integration
- Redis caching for performance
Tech Stack:
- Next.js 14 App Router
- React Server Components
- Tailwind CSS + Radix UI
- Hashnode GraphQL API
Routes:
/- Homepage with hero and featured content/blog- Blog post listing/blog/[slug]- Individual blog post/projects- Projects showcase/projects/[slug]- Project case study/about- About page/api/*- API routes (contact, webhooks)
Dashboard App (apps/dashboard)
Admin interface for content management and analytics.
Key Features:
- Content management (CRUD operations)
- Media library with Vercel Blob storage
- Analytics dashboard
- TipTap rich text editor
- Database management with Prisma
Tech Stack:
- Next.js 14 App Router
- Prisma ORM
- SQLite (dev) / PostgreSQL (prod)
- Vercel Blob storage
- TipTap editor
Routes:
/dashboard- Main dashboard/dashboard/posts- Manage posts/dashboard/media- Media library/dashboard/analytics- Analytics view/api/*- API routes for CRUD operations
Docs App (apps/docs)
Documentation site for developers and users.
Key Features:
- MDX-powered documentation
- Syntax highlighting
- Search functionality
- Component playground
Package Architecture
UI Package (packages/ui)
Shared React components using Radix UI primitives.
Components:
Button- Accessible button componentCard- Content card with variantsInput- Form input with validationDialog- Modal dialogsToast- Notifications
Example:
// packages/ui/src/button.tsx
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
outline: "border border-input",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
},
},
}
)
export interface ButtonProps extends VariantProps<typeof buttonVariants> {
// ...
}
Lib Package (packages/lib)
Business logic and service integrations.
Modules:
hashnode/- Hashnode API clientopenai/- OpenAI integrationemail/- Email serviceanalytics/- Analytics utilitiesvalidation/- Form validation schemas
Example:
// packages/lib/src/hashnode/client.ts
export async function getAllPosts() {
const response = await fetch(HASHNODE_API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: GET_ALL_POSTS_QUERY,
}),
})
return response.json()
}
Utils Package (packages/utils)
Pure utility functions without external dependencies.
Functions:
cn()- Tailwind class name mergerformatDate()- Date formattingslugify()- URL slug generationtruncate()- Text truncation
DB Package (packages/db)
Prisma schema and database client.
Schema:
// packages/db/prisma/schema.prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id String @id @default(cuid())
title String
content String
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Data Flow Architecture
Blog Content Flow
Dashboard Content Flow
Design Patterns
1. Server Components by Default
// apps/site/app/blog/page.tsx
// No "use client" directive = Server Component
export default async function BlogPage() {
// Can fetch data directly
const posts = await getAllPosts()
return (
<div>
{posts.map(post => (
<PostCard key={post.id} post={post} />
))}
</div>
)
}
2. Repository Pattern
Encapsulate data access logic in repository modules:
// packages/lib/src/repositories/post.ts
export class PostRepository {
async getAll() {
return prisma.post.findMany({
where: { published: true },
orderBy: { createdAt: "desc" },
})
}
async getBySlug(slug: string) {
return prisma.post.findUnique({
where: { slug },
})
}
}
3. Service Layer
Business logic separated from data access:
// packages/lib/src/services/blog.ts
import { PostRepository } from "../repositories/post"
import { CacheService } from "./cache"
export class BlogService {
constructor(
private postRepo: PostRepository,
private cache: CacheService
) {}
async getPosts() {
const cached = await this.cache.get("posts")
if (cached) return cached
const posts = await this.postRepo.getAll()
await this.cache.set("posts", posts, 3600)
return posts
}
}
4. Composition over Inheritance
Build complex UIs by composing simple components:
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Description</CardDescription>
</CardHeader>
<CardContent>
Content here
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
Performance Optimizations
1. Redis Caching
// Cache blog posts for 1 hour
const posts = await cache.get("posts", async () => {
return await getAllPosts()
}, { ttl: 3600 })
2. Image Optimization
import Image from "next/image"
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={630}
priority // Above the fold
/>
3. Dynamic Imports
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(
() => import("./HeavyComponent"),
{ ssr: false } // Client-only
)
4. Incremental Static Regeneration
export const revalidate = 3600 // Revalidate every hour
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
return <Post data={post} />
}
Security Considerations
Note:
Security Best Practices:
- Environment Variables: Never expose API keys in client code
- Input Validation: Validate all user input with Zod schemas
- SQL Injection: Use Prisma parameterized queries
- XSS Protection: React escapes by default, but be careful with
dangerouslySetInnerHTML - CSRF Protection: Next.js includes built-in protection
- Rate Limiting: Implement rate limiting on API routes
Next Steps
- Development Workflow - Learn the development process
- Coding Standards - Follow best practices
- Monorepo Structure - Navigate the workspace
- Package System - Work with shared packages
Note:
Understanding the architecture is crucial for effective development. Refer back to this guide as you work on features.