Architecture - adelinaenache/MenuMaker GitHub Wiki

C4 diagrams

The backend uses NestJS's GraphQL schema-first approach, providing robust dependency injection and built-in support for guards and interceptors. Prisma ORM handles database operations with PostgreSQL, offering type-safe operations and automatic migrations. JWT authentication with refresh tokens ensures secure session management and prevents unauthorized access.

The infrastructure is built on AWS ECS Fargate for serverless container orchestration, enabling automatic scaling based on demand. GitHub Actions manages the CI/CD pipeline, implementing blue-green deployments for zero downtime. AWS Secrets Manager securely stores sensitive information.

The development workflow is automated with ESLint and Prettier for consistent code style, Jest for comprehensive testing, and GraphQL Codegen for type safety. Prisma migrations manage database versioning.

Clean architecture

The codebase follows Clean Architecture principles by organizing code into layers:

  • the domain layer that is handled by the Prisma Client
  • the application layer that is implemented in the specific layers.
  • the presentation layer that contains graphql resolvers.

DTO pattern

The project implements the DTO pattern to handle data transfer between layers:

  • the Input DTOs that define the shape of incoming data and handle validation
  • the Output DTOs that define the shape of outgoing data
  • the Transformation layer that converts between DTOs and domain entities

System Diagram

structurizr-102966-SystemContext

Container Diagrams

structurizr-102966-Containers

Component Diagrams

structurizr-102966-APIComponents

Non-Functional Requirements

Performance

The application uses Next.js Server-Side Rendering (SSR) to deliver optimal performance. Initial page loads are optimized to be fast thanks to SSR, which renders pages on the server before sending them to the client. This approach ensures that users see content immediately, even before JavaScript is loaded and executed. Subsequent interactions are handled by client-side JavaScript. The backend API maintains sub-500ms response times for critical operations.

Scalability

The application is built with robust scalability in mind, using AWS infrastructure for optimal performance. The backend runs on AWS ECS with Fargate, providing serverless container orchestration that automatically scales based on demand. Each environment has dedicated ECS clusters, allowing for isolated scaling and resource management.

The system utilizes Application Load Balancers (ALB) to distribute traffic across backend containers. Health checks are performed on the /health API endpoint to ensure only healthy instances receive traffic. Currently configured with one running task per service, the architecture supports auto-scaling based on traffic patterns, allowing the system to handle increased loads by dynamically adding more container instances.

Frontend scalability is managed by Vercel, providing automatic global CDN distribution and edge network optimization.

The architecture supports horizontal scaling through:

  • Dynamic container scaling in ECS Fargate
  • Load balancing across multiple container instances
  • Automatic scaling based on traffic patterns
  • Global CDN distribution for frontend assets

Failover

The application is built with comprehensive failover mechanisms to ensure availability and reliability. The AWS infrastructure provides multiple layers of fault tolerance:

  1. Backend Failover

    • ECS Fargate automatically replaces failed containers
    • Application Load Balancers perform health checks and route traffic away from unhealthy instances
    • Blue-green deployments implemented in gh actions ensure zero downtime during updates
  2. Database Failover

    • Database instances can be configured with automatic backups and point-in-time recovery
  3. Frontend Resilience

    • Vercel's global CDN provides automatic failover between edge locations
    • Frontend caching strategies reduce dependency on backend services
    • Optimistic UI updates offered by Apollo GraphQL provide immediate feedback during network issues
  4. Monitoring and Recovery

    • CloudWatch monitors system health and triggers alerts for failures
    • Automated recovery processes for common failure scenarios

Security

Authentication is handled through a JWT-based system, where users are required to provide their email and password (as seen in the SignupInput DTO). The system uses class-validator decorators to ensure that email addresses are properly formatted and passwords are not empty. We could easily add more validators. The application implements proper session management through localStorage, where access tokens are stored with secure flags. When users log out, their tokens are properly revoked, ensuring that no unauthorized access can occur. We also implemented a refresh token mechanism, so that the sessions can be extended without a security risk.

GraphQL mutations include optimistic updates, which not only improve user experience by providing immediate feedback but also enhance security by preventing race conditions and double submissions.

On the frontend, we're using React components that offer XSS protection through proper escaping, and CSRF protection is handled by Apollo Client's built-in mechanisms.

Via the Nest guard such as @UseGuards(GqlAuthGuard) it's easy to control access on specific resources.

Maintainability

The backend uses NestJS guards to maintain clean and organized code. For example, decorators such as @UseGuards(GqlAuthGuard) decorator ensures that only authenticated users can access certain GraphQL operations, like creating a restaurant. This separation of concerns keeps the business logic clean and makes it easier to modify authentication rules without touching the core business logic.

Code quality is maintained through automated tools. ESLint and Prettier are configured to format code on save, ensuring consistent style across the codebase. This means that developers don't need to worry about formatting the code is automatically prettified when saved, reducing merge conflicts and maintaining readability.

The frontend components are organized in a dedicated @components folder, which helps maintain consistent styling and reusability. For example, UI elements like buttons, forms, and navigation are grouped together, making it easy to change a component such as a button and see it changing across the codebase.

The usage of Prisma comes with two benefits of maintainability: First, it's easy to change the underlying database if ever needed. Secondly, it keeps a consistent model that is updated via the generated migrations. The migrations are deployed whenever the docker container is built.