-
Notifications
You must be signed in to change notification settings - Fork 0
Codebase Structure
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.
At the highest level, the codebase is organized into three main sections:
-
Backend API (
src/api/
): A Node.js serverless application built with Fastify -
Frontend UI (
src/ui/
): A React application using the Mantine component library -
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.
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
A fundamental design pattern in our architecture is that routes serve as thin HTTP wrappers around core business functions. This pattern:
- Keeps business logic independent of the HTTP layer
- Makes functions more testable
- 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.
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.
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.
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.
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
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.
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.
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
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.
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.
Several key design patterns are employed throughout the codebase:
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.
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.
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.
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.
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.
The codebase follows several consistent coding conventions:
- TypeScript Throughout: Both frontend and backend are written in TypeScript
-
ESM Modules: The codebase uses ECMAScript modules (
import
/export
) syntax - Async/Await: Asynchronous code uses async/await for clarity
- Named Exports: Most modules use named exports rather than default exports
- Functional Style: A functional programming style is preferred where appropriate
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.
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.