Back to list
omerakben

data-architecture

by omerakben

1🍴 0📅 Dec 12, 2025

SKILL.md


name: data-architecture description: Single source of truth patterns, facts.ts structure, type safety, and data helper functions. Use when working with project data or adding new facts.

Data Architecture Skill

Overview

This project uses a single source of truth pattern for all data. All personal information, project details, and configuration data is centralized in typed data files.

Core Principle: Single Source of Truth

Rule: Never hardcode personal data. Always reference data files.

// ❌ WRONG - Hardcoded data
const email = "me@omerakben.com";
const name = "Omer Akben";

// ✅ CORRECT - Reference data file
import { facts } from "@/data/facts";
const email = facts.contact.email;
const name = facts.personal.name;
```typescript

## Data File Structure

### Primary Data Files

```typescript
src/data/
├── facts.ts          # Personal info, skills, experience
├── projects.ts       # Project catalog with metadata
└── ...               # Other data files

src/config/
└── assistantFaq.ts   # FAQ and intent libraries
```typescript

## facts.ts Architecture

**Location:** `src/data/facts.ts`

### Structure Overview

```typescript
export const facts = {
  personal: {
    name: string;
    title: string;
    location: LocationInfo;
    bio: string;
    tagline: string;
  },

  contact: {
    email: string;
    phone: string;
    github: string;
    linkedin: string;
    calendly: string;
  },

  professional: {
    experience: Experience[];
    education: Education[];
    skills: Skill[];
    certifications: Certification[];
    workAuthorization: WorkAuthorization; // Added 2025-11-02
  },

  preferences: {
    timezone: string;
    availability: string;
  }
};
```typescript

### Type Definitions

```typescript
interface LocationInfo {
  city: string;
  state: string;
  country: string;
  timezone: string;
}

interface Experience {
  id: string;
  company: string;
  position: string;
  startDate: string;
  endDate: string | 'Present';
  description: string;
  technologies: string[];
  achievements: string[];
}

interface Skill {
  id: string;
  name: string;
  category: SkillCategory;
  level: SkillLevel;
  yearsOfExperience: number;
}

interface WorkAuthorization {
  status: string;
  officialTitle: string;
  sponsorshipRequired: boolean;
  employmentRestrictions: boolean;
  eligibleEmployers: string;
  proofDocument: string;
  summary: string;
}
```typescript

### Work Authorization Data

**Added:** November 2, 2025
**Location:** `facts.professional.workAuthorization`

```typescript
workAuthorization: {
  status: "U.S. Permanent Resident (Green Card)",
  officialTitle: "Lawful Permanent Resident (LPR)",
  sponsorshipRequired: false,
  employmentRestrictions: false,
  eligibleEmployers: "Any U.S. employer",
  proofDocument: "Form I-551 (Permanent Resident Card)",
  summary: "Authorized to work for any U.S. employer without restrictions or sponsorship requirements."
}
```typescript

**Usage:** AI agent references this when answering recruiter questions

## projects.ts Architecture

**Location:** `src/data/projects.ts`

### Structure

```typescript
export interface Project {
  id: string;
  slug: string;
  title: string;
  tagline: string;
  description: string;
  longDescription: string;
  technologies: Technology[];
  category: ProjectCategory;
  featured: boolean;
  showcase: boolean;
  liveUrl?: string;
  githubUrl?: string;
  imageUrl: string;
  metrics?: ProjectMetrics;
  testimonials?: Testimonial[];
}

export const projects: Project[] = [
  // Array of all projects
];
```typescript

### Helper Functions

```typescript
// Get projects by category
export function getProjectsByCategory(category: ProjectCategory): Project[] {
  return projects.filter(p => p.category === category);
}

// Get featured projects
export function getFeaturedProjects(): Project[] {
  return projects.filter(p => p.featured);
}

// Get project by slug
export function getProjectBySlug(slug: string): Project | undefined {
  return projects.find(p => p.slug === slug);
}

// Get all technologies used
export function getAllTechnologies(): Technology[] {
  const techSet = new Set<Technology>();
  projects.forEach(p => p.technologies.forEach(t => techSet.add(t)));
  return Array.from(techSet);
}
```typescript

### Usage Pattern

```typescript
import { projects, getFeaturedProjects, getProjectBySlug } from "@/data/projects";

// In component
const featured = getFeaturedProjects();
const project = getProjectBySlug(params.slug);
```typescript

## Type Safety Patterns

### 1. Const Assertions

```typescript
export const SKILL_CATEGORIES = [
  'Frontend',
  'Backend',
  'DevOps',
  'AI/ML',
] as const;

export type SkillCategory = typeof SKILL_CATEGORIES[number];
// Type is: 'Frontend' | 'Backend' | 'DevOps' | 'AI/ML'
```typescript

### Benefits

- Autocomplete in IDE
- Type checking
- Single source for both runtime and types

### 2. Branded Types

```typescript
type ProjectId = string & { readonly __brand: 'ProjectId' };
type UserId = string & { readonly __brand: 'UserId' };

// Prevents mixing up IDs
function getProject(id: ProjectId) { /* ... */ }
function getUser(id: UserId) { /* ... */ }

// TypeScript error: types are incompatible
const projectId: ProjectId = 'proj_123' as ProjectId;
const userId: UserId = 'user_456' as UserId;
getProject(userId); // ❌ Error!
```typescript

### 3. Discriminated Unions

```typescript
type Education =
  | { type: 'degree'; university: string; degree: string; major: string; }
  | { type: 'bootcamp'; program: string; completion: string; }
  | { type: 'certification'; name: string; issuer: string; };

function formatEducation(edu: Education): string {
  switch (edu.type) {
    case 'degree':
      return `${edu.degree} in ${edu.major} from ${edu.university}`;
    case 'bootcamp':
      return `${edu.program}`;
    case 'certification':
      return `${edu.name} by ${edu.issuer}`;
  }
}
```typescript

## Zod Validation Integration

### Schema Definitions

**Location:** `src/lib/agent-tools/schemas.ts`

```typescript
import { z } from 'zod';

export const ContactSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  company: z.string().optional(),
  message: z.string().min(10),
});

export type Contact = z.infer<typeof ContactSchema>;
```typescript

### Runtime Validation

```typescript
// API route handler
export async function POST(request: Request) {
  const body = await request.json();

  // Validate with Zod
  const result = ContactSchema.safeParse(body);

  if (!result.success) {
    return Response.json({
      success: false,
      error: result.error.format(),
    }, { status: 400 });
  }

  // Type-safe data access
  const { name, email, company, message } = result.data;
  // ...
}
```typescript

### Benefits of Zod

- Runtime validation
- Type inference from schemas
- Detailed error messages
- Parse, transform, and validate in one step

## AI Agent Data Integration

### Knowledge Base

**Location:** `src/lib/agent-knowledge-base.ts`

The AI agent consumes data from facts.ts:

```typescript
import { facts } from '@/data/facts';

export const agentKnowledgeBase = `
# Core Identity
Name: ${facts.personal.name}
Title: ${facts.personal.title}
Location: ${facts.personal.location.city}, ${facts.personal.location.state}

# Work Authorization
Status: ${facts.professional.workAuthorization.status}
Sponsorship Required: ${facts.professional.workAuthorization.sponsorshipRequired}

# Skills
${facts.professional.skills.map(s => `- ${s.name} (${s.level})`).join('\n')}

# Contact
Email: ${facts.contact.email}
LinkedIn: ${facts.contact.linkedin}
`;
```typescript

### Benefits

- Single source ensures consistency
- Updates to facts.ts automatically flow to AI
- No hardcoded data in agent prompts

### Tool Implementations

AI agent tools reference data files:

```typescript
// src/app/api/tools/get-contact/route.ts
import { facts } from '@/data/facts';

export async function POST() {
  return Response.json({
    success: true,
    data: {
      email: facts.contact.email,
      linkedin: facts.contact.linkedin,
      github: facts.contact.github,
      calendly: facts.contact.calendly,
    }
  });
}
```typescript

## Adding New Data

### Step 1: Update Type Definition

```typescript
// src/data/facts.ts
interface Facts {
  personal: PersonalInfo;
  contact: ContactInfo;
  professional: ProfessionalInfo;
  newSection: NewSectionType; // Add new section
}
```typescript

### Step 2: Add Data

```typescript
export const facts: Facts = {
  // ... existing data
  newSection: {
    field1: 'value1',
    field2: 'value2',
  }
};
```typescript

### Step 3: Update AI Knowledge Base

```typescript
// src/lib/agent-knowledge-base.ts
export const agentKnowledgeBase = `
...existing content...

# New Section
${facts.newSection.field1}
${facts.newSection.field2}
`;
```typescript

### Step 4: Add Tests

```typescript
// src/data/facts.test.ts
describe('facts.newSection', () => {
  it('should have required fields', () => {
    expect(facts.newSection.field1).toBeDefined();
    expect(facts.newSection.field2).toBeDefined();
  });
});
```typescript

## Data Validation Patterns

### 1. Email Validation

```typescript
import { z } from 'zod';

const EmailSchema = z.string()
  .email('Invalid email format')
  .toLowerCase()
  .trim();

// Usage
const email = EmailSchema.parse('USER@EXAMPLE.COM');
// Result: 'user@example.com'
```typescript

### 2. URL Validation

```typescript
const UrlSchema = z.string()
  .url('Invalid URL format')
  .refine(url => url.startsWith('https://'), {
    message: 'URL must use HTTPS'
  });
```typescript

### 3. Date Validation

```typescript
const DateSchema = z.string()
  .regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be YYYY-MM-DD')
  .refine(date => !isNaN(Date.parse(date)), {
    message: 'Invalid date'
  });
```typescript

### 4. Enum Validation

```typescript
const SkillLevelSchema = z.enum([
  'Beginner',
  'Intermediate',
  'Advanced',
  'Expert'
]);

type SkillLevel = z.infer<typeof SkillLevelSchema>;
```typescript

## Helper Function Patterns

### 1. Filter Helpers

```typescript
// Get items by property
export function getByCategory<T extends { category: string }>(
  items: T[],
  category: string
): T[] {
  return items.filter(item => item.category === category);
}

// Get items by multiple criteria
export function getByFilters<T>(
  items: T[],
  filters: Partial<T>
): T[] {
  return items.filter(item => {
    return Object.entries(filters).every(([key, value]) => {
      return item[key as keyof T] === value;
    });
  });
}
```typescript

### 2. Sort Helpers

```typescript
// Sort by date
export function sortByDate<T extends { date: string }>(
  items: T[],
  order: 'asc' | 'desc' = 'desc'
): T[] {
  return [...items].sort((a, b) => {
    const dateA = new Date(a.date).getTime();
    const dateB = new Date(b.date).getTime();
    return order === 'asc' ? dateA - dateB : dateB - dateA;
  });
}

// Sort by property
export function sortBy<T, K extends keyof T>(
  items: T[],
  key: K,
  order: 'asc' | 'desc' = 'asc'
): T[] {
  return [...items].sort((a, b) => {
    if (a[key] < b[key]) return order === 'asc' ? -1 : 1;
    if (a[key] > b[key]) return order === 'asc' ? 1 : -1;
    return 0;
  });
}
```typescript

### 3. Transform Helpers

```typescript
// Group by property
export function groupBy<T, K extends keyof T>(
  items: T[],
  key: K
): Record<string, T[]> {
  return items.reduce((acc, item) => {
    const groupKey = String(item[key]);
    if (!acc[groupKey]) acc[groupKey] = [];
    acc[groupKey].push(item);
    return acc;
  }, {} as Record<string, T[]>);
}

// Map to lookup object
export function toMap<T extends { id: string }>(
  items: T[]
): Record<string, T> {
  return items.reduce((acc, item) => {
    acc[item.id] = item;
    return acc;
  }, {} as Record<string, T>);
}
```typescript

## Testing Data Integrity

### Unit Tests

```typescript
// src/data/facts.test.ts
import { facts } from './facts';

describe('facts.ts data integrity', () => {
  it('should have valid email', () => {
    expect(facts.contact.email).toMatch(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
  });

  it('should have all required skills', () => {
    expect(facts.professional.skills.length).toBeGreaterThan(0);
    facts.professional.skills.forEach(skill => {
      expect(skill.name).toBeTruthy();
      expect(skill.category).toBeTruthy();
      expect(skill.level).toBeTruthy();
    });
  });

  it('should have valid work authorization', () => {
    const { workAuthorization } = facts.professional;
    expect(workAuthorization.status).toBeTruthy();
    expect(typeof workAuthorization.sponsorshipRequired).toBe('boolean');
    expect(typeof workAuthorization.employmentRestrictions).toBe('boolean');
  });
});
```typescript

### Zod Schema Tests

```typescript
import { ContactSchema } from './schemas';

describe('ContactSchema validation', () => {
  it('should accept valid contact', () => {
    const valid = {
      name: 'John Doe',
      email: 'john@example.com',
      message: 'Hello, this is a test message.',
    };

    const result = ContactSchema.safeParse(valid);
    expect(result.success).toBe(true);
  });

  it('should reject invalid email', () => {
    const invalid = {
      name: 'John Doe',
      email: 'not-an-email',
      message: 'Hello, world',
    };

    const result = ContactSchema.safeParse(invalid);
    expect(result.success).toBe(false);
  });
});
```typescript

## Common Mistakes

### Mistake 1: Hardcoding Data

```typescript
// ❌ WRONG
function Header() {
  return <h1>Omer Akben - AI Engineer</h1>;
}

// ✅ CORRECT
import { facts } from '@/data/facts';

function Header() {
  return <h1>{facts.personal.name} - {facts.personal.title}</h1>;
}
```typescript

### Mistake 2: Inconsistent Data Format

```typescript
// ❌ WRONG - Different date formats
{
  startDate: '2024-01-01',
  endDate: 'January 2024',
}

// ✅ CORRECT - Consistent ISO format
{
  startDate: '2024-01-01',
  endDate: '2024-01-31',
}
```typescript

### Mistake 3: No Validation

```typescript
// ❌ WRONG - Assumes data is valid
const user = JSON.parse(request.body);
saveUser(user);

// ✅ CORRECT - Validate first
const result = UserSchema.safeParse(JSON.parse(request.body));
if (result.success) {
  saveUser(result.data);
}
```typescript

## Data Update Checklist

When adding or updating data:

- [ ] Update type definitions first
- [ ] Add/modify data in facts.ts or projects.ts
- [ ] Update AI knowledge base if needed
- [ ] Add helper functions if beneficial
- [ ] Write unit tests for new data
- [ ] Verify AI agent responses still accurate
- [ ] Check that related tools still work
- [ ] Update documentation if structure changed

## Quick Reference

```typescript
// Import data
import { facts } from '@/data/facts';
import { projects, getProjectBySlug } from '@/data/projects';

// Access data
const email = facts.contact.email;
const name = facts.personal.name;
const project = getProjectBySlug('portfolio');

// Validate with Zod
const result = Schema.safeParse(data);
if (result.success) {
  // Use result.data
}

// Helper pattern
export function getFiltered<T>(items: T[], predicate: (item: T) => boolean): T[] {
  return items.filter(predicate);
}
```typescript

## Related Files

- `src/data/facts.ts` - Single source of truth for personal data
- `src/data/projects.ts` - Project catalog with helpers
- `src/lib/agent-knowledge-base.ts` - AI agent data integration
- `src/lib/agent-tools/schemas.ts` - Zod validation schemas
- `src/config/assistantFaq.ts` - FAQ and intent libraries

Score

Total Score

55/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新

+5
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon