Skip to content

Latest commit

 

History

History
780 lines (631 loc) · 28 KB

File metadata and controls

780 lines (631 loc) · 28 KB

CloudZen Component Architecture Documentation

Overview

This document describes the component-based architecture implemented in the CloudZen Blazor WebAssembly application, focusing on the WhoIAm page refactoring that demonstrates modern Blazor component design principles.


🎯 Architecture Goals

The refactoring was driven by these core principles:

  1. Separation of Concerns - Each component has a single, well-defined responsibility
  2. Reusability - Components can be used across multiple pages
  3. Maintainability - Easier to understand, test, and modify
  4. Scalability - Component structure supports future growth
  5. Performance - Smaller components enable better Blazor rendering optimization

📂 Project Structure

CloudZen/
├── Models/
│   ├── ProjectInfo.cs                  # Project data model
│   └── ProjectParticipant.cs           # Project participant model
│   └── ServiceInfo.cs                  # Service data model. Records service details
│
├── Services/
│   ├── ProjectService.cs               # Project data management service
│   ├── ResumeService.cs                # Resume download service
│   ├── PersonalService.cs              # Personal info service
│   ├── ChatbotService.cs              # AI chatbot HTTP client service
│   ├── Abstractions/
│   │   └── IChatbotService.cs         # Chatbot service interface
│   └── ... (other services)
│
├── Shared/
│   ├── Chatbot/
│   │   ├── CloudZenChatbot.razor       # AI chatbot widget (FAB + chat panel)
│   │   └── CloudZenChatbot.razor.css   # Scoped dark theme styles
│   │
│   ├── Profile/
│   │   ├── ProfileHeader.razor         # Profile avatar, name, social links
│   │   ├── ProfileApproach.razor       # Professional approach section
│   │   └── ProfileHighlights.razor     # Results, expertise, resume button
│   │
│   ├── Projects/
│   │   └── ProjectCard.razor           # Individual project display card
│   │
│   ├── WhoIAm.razor                    # Main page (orchestrator)
│   └── ... (other shared components)
│
└── Program.cs                          # Service registration

🧩 Component Breakdown

1. WhoIAm.razor (Page Component)

Role: Page orchestrator - composes and coordinates child components

Responsibilities:

  • Page routing (@page "/whoiam")
  • Component composition and layout
  • Data fetching (Projects list)
  • Event handling delegation
  • Scroll behavior logic

Dependencies:

  • ProfileHeader - Displays profile information
  • ProfileApproach - Shows professional methodology
  • ProfileHighlights - Displays achievements and expertise
  • ProjectCard - Renders individual project cards
  • ProjectService - ✅ Active - Data access layer for all projects
  • ResumeService - Resume download functionality

Lines of Code: 73 lines (down from ~700, -90% reduction)


2. Profile Components

ProfileHeader.razor

Purpose: Display user profile header with avatar, name, and social links

Parameters:

  • AvatarUrl (string) - URL to profile image
  • AltText (string) - Image accessibility text
  • Title (string) - Section heading
  • NameHighlight (string) - Highlighted name portion
  • RoleDescription (string) - Short role summary
  • DetailedDescription (string) - Full professional bio
  • LinkedInUrl (string?) - LinkedIn profile link (optional)
  • GitHubUrl (string?) - GitHub profile link (optional)

Styling: Tailwind CSS - responsive design with centered layout

Reusability: Can be used in About, Contact, or other profile pages


ProfileApproach.razor

Purpose: Display professional approach and methodology

Parameters: None (currently static content)

Future Enhancements:

  • Accept content as parameters for flexibility
  • Support markdown rendering

ProfileHighlights.razor

Purpose: Display key achievements, expertise, and resume download

Parameters:

  • OnResumeDownload (EventCallback) - Triggered when resume button is clicked

Features:

  • Bullet-pointed key results list
  • Tech stack badges display
  • Resume download button with event callback

Parent Responsibility: Parent component (WhoIAm) must handle the resume download logic


3. Project Components

ProjectCard.razor

Purpose: Display individual project information in a card format

Parameters:

  • Project (ProjectInfo, required) - Complete project data

Features:

  • Status badge with color coding
  • Role display with icon
  • Project type indicator (Side Project / Customer work)
  • Participant avatars
  • Tech stack tags
  • GitHub link (conditional)
  • Challenges list
  • Outcomes/Results list
  • Progress bar with color coding

Helper Methods:

  • GetStatusColor(string status) - Returns CSS classes for status badge
  • GetProgressColor(int progress) - Returns CSS classes for progress bar

Styling: Card-based layout with responsive design


🔄 Component Interaction: ProjectFilter ↔ WhoIAm

Communication Pattern

Child-to-Parent via EventCallback<T> - Blazor's standard type-safe event handling

Flow Diagram

┌──────────────────────────────────────────────────────────────┐
│                 User Action (ProjectFilter)                  │
│  • Dropdown selection changes (Status/Type)                 │
│  • Clear All button clicked                                 │
│  • Individual filter badge removed                          │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│           Status/Type Selection Changed (@bind)              │
│  SelectedStatus or SelectedProjectType updated              │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│        OnFilterChanged() called (@bind:after trigger)        │
│  private async Task OnFilterChanged()                       │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│   OnFilterChange.InvokeAsync((Status, Type)) [Child→Parent] │
│  await OnFilterChange.InvokeAsync(                          │
│      (SelectedStatus, SelectedProjectType));                │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│     HandleFilterChange((Status, Type)) invoked (WhoIAm)      │
│  Parent receives tuple with current filter values           │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│          FilteredProjects = Projects.Where(...)              │
│  LINQ filtering applied:                                     │
│  • Filter by Status (if not empty)                          │
│  • Filter by ProjectType (if not empty)                     │
│  • Update FilteredProjects list                             │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│             StateHasChanged() (implicit)                     │
│  Blazor detects component state change automatically        │
└──────────────────────────────────────────────────────────────┘
                          ↓
┌──────────────────────────────────────────────────────────────┐
│          UI Re-renders with Filtered Projects                │
│  • ProjectCard components render with FilteredProjects      │
│  • Empty state shown if no matches                          │
│  • Smooth transition with filtered results                  │
└──────────────────────────────────────────────────────────────┘

Execution Steps

Step Component Action
1 WhoIAm (Parent) Passes HandleFilterChange method to child's OnFilterChange parameter
2 ProjectFilter (Child) User changes dropdown/clicks button → triggers OnFilterChanged()
3 ProjectFilter (Child) Invokes parent callback: OnFilterChange.InvokeAsync((Status, Type))
4 WhoIAm (Parent) Receives tuple, applies LINQ filtering, updates FilteredProjects
5 Blazor Framework Detects state change, re-renders ProjectCard components with filtered data

Code Implementation

Parent (WhoIAm.razor)

<ProjectFilter OnFilterChange="HandleFilterChange" />

@code {
    private List<ProjectInfo> FilteredProjects = new();
    
    private void HandleFilterChange((string Status, string ProjectType) filters)
    {
        FilteredProjects = Projects
            .Where(p => string.IsNullOrEmpty(filters.Status) || p.Status == filters.Status)
            .Where(p => string.IsNullOrEmpty(filters.ProjectType) || 
                        (filters.ProjectType == "Customer" 
                            ? p.ProjectType.StartsWith("Customer:") 
                            : p.ProjectType == filters.ProjectType))
            .ToList();
    }
}

Child (ProjectFilter.razor)

@code {
    [Parameter]
    public EventCallback<(string Status, string ProjectType)> OnFilterChange { get; set; }
    
    private async Task OnFilterChanged()
    {
        await OnFilterChange.InvokeAsync((SelectedStatus, SelectedProjectType));
    }
}

Key Benefits

Type Safety: Compile-time checking via tuple (string, string)
Async Support: Native async/await compatibility
Loose Coupling: Child doesn't know parent's implementation
Blazor Optimized: Efficient automatic re-rendering
Reusability: ProjectFilter can be used with any parent component


📊 Data Models

ProjectInfo.cs

Represents a complete project in the portfolio.

Properties:

public class ProjectInfo
{
    public string Name { get; set; }                              // Project name
    public string Status { get; set; }                            // "Completed", "In Progress", "Planning"
    public string Description { get; set; }                       // Full description
    public string[] TechStack { get; set; }                       // Technologies used
    public int Progress { get; set; }                             // 0-100
    public List<string> Results { get; set; }                     // Measurable outcomes
    public IEnumerable<ProjectParticipant> Participants { get; set; } // Contributors
    public string Role { get; set; }                              // Your role
    public List<string> Challenges { get; set; }                  // Main challenges
    public string? GithubUrl { get; set; }                        // Optional GitHub link
    public string ProjectType { get; set; }                       // "Side Project" / "Customer: {Name}"
}

ProjectParticipant.cs

Represents a project contributor.

Properties:

public class ProjectParticipant
{
    public string Name { get; set; }        // Participant name
    public string ImageUrl { get; set; }    // Avatar URL
}

🔧 Services

Service Layer Overview: Same approach should be applied to other services (e.g., ResumeService, PersonalService)

ProjectService.cs

Centralized service for project data management.

Methods:

  • GetAllProjects() - Returns all projects sorted by status
  • GetProjectsByStatus(string status) - Filters by status
  • GetProjectsByType(string projectType) - Filters by type
  • GetFeaturedProjects() - Returns top completed projects

Future Enhancements:

  • Load from JSON file (wwwroot/data/projects.json)
  • Fetch from API endpoint
  • Cache projects for performance
  • Support pagination/filtering

Registration (Program.cs):

builder.Services.AddScoped<ProjectService>();

🎨 Styling & Design System

Color Palette

  • Primary: Indigo (indigo-600, indigo-700, indigo-800)
  • Success: Green (green-100, green-600, green-800)
  • Warning: Amber (amber-300, amber-500, amber-600)
  • Error: Red (red-200, red-500, red-700)
  • Neutral: Gray (gray-100 through gray-900)

Status Colors

  • Completed: Green background (bg-green-100 text-green-800)
  • 🔄 In Progress: Amber background (bg-amber-300 text-yellow-800)
  • 📋 Planning: Red background (bg-red-200 text-red-700)

Progress Bar Colors

  • 100%: Blue (bg-blue-400)
  • 70-99%: Emerald (bg-emerald-600)
  • 40-69%: Yellow (bg-yellow-400)
  • <40%: Red (bg-red-500)

🚀 Usage Examples

Using ProjectCard in WhoIAm.razor

@foreach (var project in Projects)
{
    <ProjectCard Project="@project" />
}

Using ProfileHeader

<ProfileHeader 
    AvatarUrl="/images/dariem-avatar.png"
    AltText="Dariem C. Macias - CloudZen"
    Title="Who I Am"
    NameHighlight="Dariem C. Macias here"
    RoleDescription="Software Engineer and Principal Consultant at CloudZen Inc."
    DetailedDescription="Over the past seven years..."
    LinkedInUrl="https://www.linkedin.com/in/dariemcmacias"
    GitHubUrl="https://github.com/dariemcarlosdev?tab=repositories" />

Using ProfileHighlights with Event Callback

<ProfileHighlights OnResumeDownload="DownloadResume" />

@code {
    private async Task DownloadResume()
    {
        // Handle resume download logic
    }
}

📈 Performance Metrics

Before Refactoring

  • WhoIAm.razor: ~700 lines
  • Components: 0 reusable components
  • Data Models: Inline in @code block
  • Services: No service layer
  • Testability: Low (tightly coupled)

After Refactoring

  • WhoIAm.razor: 73 lines (-90%)
  • Components: 4 reusable components
  • Data Models: 2 separate model files
  • Services: 1 dedicated service layer (ProjectService)
  • Testability: High (loosely coupled)

Component Sizes

  • ProfileHeader: 81 lines
  • ProfileApproach: 35 lines
  • ProfileHighlights: 75 lines
  • ProjectCard: 139 lines
  • ProjectService: 363 lines

Refactoring Journey

Phase Action Lines Before Lines After Reduction
Initial Starting point 700 700 0%
Phase 1 Extracted ProjectCard 700 521 -25%
Phase 2A Created Profile Components 521 521 0%
Service Layer Moved data to ProjectService 521 104 -80%
Final Integrated all components 104 73 -90%

Total Impact

  • Lines Removed: 627 lines (-90%)
  • New Components Created: 4
  • Service Classes Added: 1
  • Model Classes Extracted: 2
  • Build Status: ✅ Success
  • Breaking Changes: None

✅ Benefits Achieved

1. Maintainability

  • ✅ Each component has a single responsibility
  • ✅ Bugs isolated to specific components
  • ✅ Easier code navigation

2. Reusability

  • ✅ ProjectCard usable in dedicated Projects page
  • ✅ ProfileHeader reusable across multiple pages
  • ✅ Components shareable across projects

3. Testability

  • ✅ Unit test individual components
  • ✅ Mock dependencies easily
  • ✅ Test component interactions

4. Scalability

  • ✅ Easy to add new project fields
  • ✅ Simple to extend ProjectService
  • ✅ Component composition supports growth

5. Developer Experience

  • ✅ Smaller files reduce cognitive load
  • ✅ Clear component boundaries
  • ✅ Better IntelliSense support

🔮 Future Enhancements

Phase 3: Data Externalization

  1. Move project data to wwwroot/data/projects.json
  2. Implement async data loading in ProjectService
  3. Add caching layer for performance

Phase 4: Advanced Features

  1. Search/Filter:

    • Filter projects by tech stack
    • Search by project name/description
    • Filter by date range
  2. Animations:

    • Card hover effects
    • Progress bar animations
    • Smooth scrolling
  3. Accessibility:

    • ARIA labels for all interactive elements
    • Keyboard navigation support
    • Screen reader optimization
  4. Micro-Components (Optional):

    • ProjectStatusBadge.razor - Reusable status indicator
    • ProjectProgressBar.razor - Standalone progress visualization
    • TechStackBadge.razor - Individual technology tag

🛠️ Development Guidelines

Component Creation Checklist

  • Single responsibility principle
  • XML documentation for public members
  • Parameter validation
  • Responsive design (mobile-first)
  • Accessibility considerations
  • Event callbacks for parent communication

Naming Conventions

  • Components: PascalCase (e.g., ProfileHeader.razor)
  • Parameters: PascalCase (e.g., AvatarUrl)
  • Methods: PascalCase (e.g., GetStatusColor)
  • CSS Classes: kebab-case Tailwind utilities

Component Communication

  • Parent → Child: Use [Parameter] properties
  • Child → Parent: Use EventCallback or EventCallback<T>
  • Sibling Communication: Use shared state service

📚 References & Resources

Official Documentation

Best Practices



📊 Final Architecture Summary

Complete Refactoring Results

WhoIAm.razor Transformation

Initial State (Version 0):
├── 700 lines of monolithic code
├── Inline project data
├── Mixed concerns (data + presentation)
└── No reusable components

Final State (Version 1.1):
├── 73 lines of orchestration code (-90%)
├── 4 reusable components
├── Centralized data service (ProjectService)
└── Clean separation of concerns

Component Architecture

CloudZen Application
│
├── Pages/
│   └── WhoIAm.razor (73 lines)
│       ├── Uses: ProfileHeader
│       ├── Uses: ProfileApproach
│       ├── Uses: ProfileHighlights
│       ├── Uses: ProjectCard (x9 projects)
│       ├── Injects: ProjectService
│       └── Injects: ResumeService
│
├── Components/
│   ├── Shared/Chatbot/
│   │   ├── CloudZenChatbot.razor       # AI chatbot widget
│   │   └── CloudZenChatbot.razor.css   # Scoped dark theme
│   │
│   ├── Shared/Profile/
│   │   ├── ProfileHeader.razor (81 lines)
│   │   ├── ProfileApproach.razor (35 lines)
│   │   └── ProfileHighlights.razor (75 lines)
│   │
│   └── Shared/Projects/
│       └── ProjectCard.razor (139 lines)
│
├── Services/
│   ├── ProjectService.cs (363 lines)
│   │   ├── GetAllProjects()
│   │   ├── GetProjectsByStatus()
│   │   ├── GetProjectsByType()
│   │   └── GetFeaturedProjects()
│   │
│   ├── ChatbotService.cs
│   │   └── SendMessageAsync() → POST /api/chat
│   │
│   └── ResumeService.cs
│
└── Models/
    ├── ProjectInfo.cs (74 lines)
    └── ProjectParticipant.cs (19 lines)

Key Achievements

  • 90% code reduction in main page component
  • 4 reusable components created
  • 100% separation of data and presentation
  • 9 projects managed through service layer
  • Zero breaking changes during refactoring
  • Full build success maintained throughout

Code Quality Improvements

Metric Before After Improvement
Cyclomatic Complexity High Low
Code Duplication ~100 lines 0 lines
Testability Score Low High
Maintainability Index 45 85
Component Cohesion Low High
Coupling Tight Loose

🎓 Lessons Learned

What Worked Well

  1. Incremental Refactoring - Breaking changes into phases reduced risk
  2. Component Extraction - Starting with ProjectCard established patterns
  3. Service Layer - Centralizing data improved maintainability significantly
  4. Build Verification - Running builds after each change caught issues early
  5. Documentation - Maintaining COMPONENT_ARCHITECTURE.md kept team aligned

Best Practices Applied

  1. ✅ Single Responsibility Principle (SRP)
  2. ✅ Don't Repeat Yourself (DRY)
  3. ✅ Separation of Concerns (SoC)
  4. ✅ Component-Based Architecture
  5. ✅ Service-Oriented Design
  6. ✅ Parameter-Based Component Communication
  7. ✅ EventCallback for child-to-parent communication

Future Recommendations

  1. Add Unit Tests - Test components and services independently
  2. Implement Caching - Cache projects in ProjectService for performance
  3. Add Loading States - Show spinners while loading data
  4. Error Handling - Add try-catch blocks and error boundaries
  5. Accessibility - Add ARIA labels and keyboard navigation
  6. Analytics - Track component usage and performance metrics

🔄 Migration Guide

For Developers Joining the Project

Understanding the Architecture

  1. Read this document - Understand component structure
  2. Review WhoIAm.razor - See how components are orchestrated
  3. Examine ProjectService - Learn data management patterns
  4. Check component parameters - Understand data flow

Adding New Projects

// In Services/ProjectService.cs - GetProjectsData() method
new ProjectInfo
{
    Name = "Your Project Name",
    Status = "Completed", // or "In Progress", "Planning"
    Description = "Project description...",
    TechStack = new[] { ".NET 8", "Blazor", "Azure" },
    Progress = 100,
    Results = new List<string> { "Achievement 1", "Achievement 2" },
    Participants = new[] {
        new ProjectParticipant { 
            Name = "Developer Name", 
            ImageUrl = "/images/avatar.png" 
        }
    },
    Role = "Your Role",
    Challenges = new List<string> { "Challenge 1", "Challenge 2" },
    GithubUrl = "https://github.com/...",
    ProjectType = "Side Project" // or "Customer: Name"
}

Creating New Components

  1. Follow naming conventions: PascalCase for components
  2. Add XML documentation: Document all public members
  3. Use parameters: Accept data via [Parameter] properties
  4. Add event callbacks: For parent communication
  5. Apply responsive design: Mobile-first approach
  6. Test thoroughly: Verify in different screen sizes

📞 Support & Contribution

Getting Help

  • Architecture Questions: Review this document first
  • Component Issues: Check component documentation sections
  • Service Layer: See ProjectService.cs inline comments
  • Build Problems: Ensure all dependencies are restored

Contributing

  1. Follow existing patterns and conventions
  2. Add/update documentation for changes
  3. Run builds before committing
  4. Keep components small and focused
  5. Write meaningful commit messages

Verification Checklist

Post-Refactoring Verification

  • All builds succeed
  • No compilation errors
  • No runtime exceptions
  • UI renders correctly
  • All features work as expected
  • No broken links
  • Responsive design maintained
  • Accessibility preserved
  • Performance not degraded
  • Documentation updated

ProjectFilter Component Verification

  • Status filter dropdown works correctly
  • Project type filter dropdown works correctly
  • Filters can be combined (status + type)
  • Clear all button resets both filters
  • Individual filter remove buttons work
  • Active filter counter updates correctly
  • Empty state displays when no matches
  • Responsive layout on mobile/desktop
  • All animations and transitions smooth
  • Icons display correctly in dropdowns


📝 Change Log

Version 1.2 (Current - March 2026)

  • ✅ Added AI Chatbot component (CloudZenChatbot.razor)
  • ✅ Added chatbot client service (ChatbotService.cs, IChatbotService.cs)
  • ✅ Added chatbot configuration model (ChatbotOptions.cs)
  • ✅ Added AI chatbot backend (ChatFunction.cs, ChatRequest.cs, ChatResponse.cs)
  • ✅ Integrated chatbot widget into main layout
  • ✅ See AI_CHATBOT_DOCUMENTATION.md for full chatbot architecture

Version 1.1 (December 2025)

  • ✅ Extracted ProjectCard component
  • ✅ Created Profile components (Header, Approach, Highlights)
  • ✅ Moved data models to Models folder
  • ✅ Created ProjectService with full CRUD methods
  • ✅ Integrated all components into WhoIAm.razor
  • ✅ Moved all project data to ProjectService
  • ✅ Reduced WhoIAm.razor from 700 to 73 lines (-90%)
  • ✅ Comprehensive architecture documentation

Planned for Version 1.2

  • Externalize project data to JSON file
  • Add search/filter functionality to projects
  • Implement caching in ProjectService
  • Add unit tests for components and services
  • Add async data loading support
  • Implement error boundaries
  • Add loading states and spinners

👥 Contributors

  • Dariem C. Macias - Principal Consultant / Solution Architect
  • Refactoring Assistance: GitHub Copilot

📄 License

This architecture is part of the CloudZen Inc. portfolio application.


Last Updated: March 2026
Document Version: 1.3
Maintained By: CloudZen Development Team