Skip to content

Codebase Structure

Dev Singh edited this page Mar 22, 2025 · 1 revision

Core follows a structured and modular architecture that separates concerns and promotes maintainability. This document explains the organization of the codebase, the purpose of each directory, and the design patterns employed throughout the system.

High-Level Architecture

At the highest level, the codebase is organized into three main sections:

  1. Backend API (src/api/): A Node.js serverless application built with Fastify
  2. Frontend UI (src/ui/): A React application using the Mantine component library
  3. Shared Code (src/common/): Code shared between frontend and backend

This separation allows for clear boundaries between the presentation layer, business logic, and shared types/utilities.

Backend Structure

The backend follows a layered architecture that separates HTTP handling from business logic:

src/api/
├── functions/     # Core business logic organized by domain
├── plugins/       # Fastify plugins for cross-cutting concerns
├── routes/        # HTTP endpoint definitions
├── sqs/           # Asynchronous task handlers
└── resources/     # Static resources used by the API

Routes as HTTP Wrappers

A fundamental design pattern in our architecture is that routes serve as thin HTTP wrappers around core business functions. This pattern:

  1. Keeps business logic independent of the HTTP layer
  2. Makes functions more testable
  3. Allows for potential reuse across different interfaces (HTTP API, CLI, etc.)

For example, in src/api/routes/events.ts, the route handler:

  • Validates HTTP inputs using Zod schemas
  • Performs authentication and authorization
  • Calls the appropriate business logic functions
  • Transforms the result into an HTTP response

The actual business logic for event management is implemented in separate function modules.

Functions Directory

The functions/ directory contains the core business logic of the application, organized by domain:

  • authorization.ts: User role and permission management
  • cache.ts: Caching mechanisms for performance optimization
  • discord.ts: Integration with Discord for event announcements
  • entraId.ts: Microsoft Entra ID integration for identity management
  • membership.ts: User membership status and verification
  • ses.ts: Email sending capabilities via AWS SES
  • stripe.ts: Payment processing via Stripe
  • validation.ts: Input validation utilities

Each function module is focused on a specific domain and is designed to be independent of the HTTP layer. This makes the core logic more testable and potentially reusable in different contexts.

Plugins Directory

The plugins/ directory contains Fastify plugins that implement cross-cutting concerns:

  • auth.ts: Authentication and authorization middleware
  • errorHandler.ts: Centralized error handling and formatting
  • rateLimiter.ts: Rate limiting to prevent abuse
  • validate.ts: Request validation using Zod schemas

These plugins are registered with the Fastify application and apply their functionality across multiple routes.

SQS Directory

The sqs/ directory handles asynchronous processing via Amazon SQS:

  • index.ts: Main entry point for the SQS Lambda function
  • handlers.ts: Individual handlers for different message types
  • logger.ts: Logging utilities specific to SQS processing

This separation allows for clear handling of asynchronous tasks outside the main request-response cycle.

Frontend Structure

The frontend follows a component-based architecture organized by feature:

src/ui/
├── components/     # Reusable UI components
├── pages/          # Page components organized by feature
├── util/           # Frontend utilities
└── Router.tsx      # Application routing

Components Directory

The components/ directory contains reusable UI components that are shared across multiple pages:

  • AppShell/: Layout components for the application shell
  • AuthContext/: Authentication context and hooks
  • AuthGuard/: Components that enforce authorization
  • Navbar/: Navigation components
  • ProfileDropdown/: User profile management UI

These components encapsulate reusable UI patterns and behaviors.

Pages Directory

The pages/ directory contains page components organized by feature:

  • events/: Event management pages
  • iam/: Identity management pages
  • profile/: User profile management
  • stripe/: Payment handling pages
  • tickets/: Ticketing and merchandise pages

Each feature area has its own directory containing the relevant page components. This organization makes it easy to find the code for a specific feature.

Shared Code Structure

The common/ directory contains code shared between the frontend and backend:

src/common/
├── errors/         # Error definitions
├── types/          # Shared TypeScript types
├── config.ts       # Configuration constants
├── orgs.ts         # Organization definitions
├── roles.ts        # Role definitions
└── utils.ts        # Shared utility functions

Types Directory

The types/ directory contains TypeScript type definitions shared between frontend and backend:

  • iam.ts: Types for identity and access management
  • msGraphApi.ts: Types for Microsoft Graph API integration
  • sqsMessage.ts: Types for SQS message payloads
  • stripe.ts: Types for Stripe integration

Sharing these types ensures consistency between frontend and backend, particularly for API request and response structures.

Errors Directory

The errors/ directory contains custom error classes used throughout the application:

  • index.ts: Defines the base error class and specific error types

These error types provide consistent error handling and formatting across the application.

Design Patterns

Several key design patterns are employed throughout the codebase:

1. Separation of Concerns

The codebase clearly separates:

  • HTTP handling (routes)
  • Business logic (functions)
  • Cross-cutting concerns (plugins)
  • UI components (components)
  • Page-level features (pages)

This separation makes the code more maintainable and testable.

2. Dependency Injection

The Fastify framework uses dependency injection to provide access to shared resources:

  • Database clients
  • Configuration settings
  • Authentication utilities
  • Logging services

This pattern makes it easy to mock dependencies for testing while reusing clients for performance.

3. Plugin Architecture

Fastify's plugin architecture is used extensively to modularize functionality:

  • Each route file is a plugin that registers routes
  • Cross-cutting concerns are implemented as plugins
    • For example, authentication and authorization are handled via plugins
  • Cross-cutting business functions are implemented as functions

This architecture makes it easy to compose functionality and maintain separation of concerns.

4. Zod Schema Validation

Zod schemas are used throughout the codebase for validation:

  • API request validation
  • SQS message payload validation
  • Shared type definitions between frontend and backend

This approach ensures type safety and consistent validation.

5. Repository Pattern

Database access follows a repository-like pattern, with:

  • Clear separation of data access logic
  • Domain-specific query functions
  • Consistent error handling

This pattern simplifies testing and makes it easier to modify the data layer.

Coding Conventions

The codebase follows several consistent coding conventions:

  1. TypeScript Throughout: Both frontend and backend are written in TypeScript
  2. ESM Modules: The codebase uses ECMAScript modules (import/export) syntax
  3. Async/Await: Asynchronous code uses async/await for clarity
  4. Named Exports: Most modules use named exports rather than default exports
  5. Functional Style: A functional programming style is preferred where appropriate

Testing Structure

The testing structure mirrors the main codebase organization:

tests/
├── unit/            # Unit tests for individual functions
│   ├── api/         # Backend unit tests
│   └── ui/          # Frontend component tests
│
├── integration/     # Integration tests for API with services
│   ├── dynamodb/    # DynamoDB integration tests
│   ├── entra/       # Microsoft Entra ID integration tests
│   └── stripe/      # Stripe integration tests
│
├── e2e/             # End-to-end tests
│   ├── auth/        # Authentication flow tests
│   ├── events/      # Event management workflow tests
│   └── tickets/     # Ticketing workflow tests
│
└── fixtures/        # Test data and fixtures
    ├── events/      # Sample event data
    └── users/       # Test user accounts

This structure ensures that tests are organized in a way that matches the code they're testing, making it easier to find and maintain tests.

Deployment Resources

The cloudformation/ directory contains AWS CloudFormation templates that define the infrastructure:

cloudformation/
├── custom-domain.yml   # Custom domain configuration
├── iam.yml             # IAM roles and policies
├── logs.yml            # CloudWatch logs configuration
├── main.yml            # Main stack template
├── phony-swagger.yml   # API Gateway configuration
└── sqs.yml             # SQS queues configuration

These templates define the entire infrastructure needed to deploy the application.