Code Structure
This guide explains how the Plus261 codebase is organized and how to navigate it effectively.
What You’ll Learn
Section intitulée « What You’ll Learn »By the end of this guide, you’ll understand:
- ✅ The monorepo structure and organization
- ✅ How modules are structured and organized
- ✅ The different types of modules (CRUD, custom resolvers, services)
- ✅ How to create a new module
- ✅ Naming conventions and import patterns
- ✅ Best practices for code organization
Quick Navigation
Section intitulée « Quick Navigation »| What You Need | Where to Find It |
|---|---|
| 📁 API Code | apps/api/src/ |
| 🌐 Web Code | apps/web/src/ |
| 📊 Database Schema | libs/db/src/schema/ |
| 📚 Shared Libraries | libs/ |
| 📖 API Documentation | /api/* |
| 🛠️ Type GraphQL Query Docs | /type-graphql-query/* |
Project Structure
Section intitulée « Project Structure »The project follows a monorepo structure with multiple applications and libraries:
plus261/├── apps/│ ├── api/ → GraphQL API server│ ├── web/ → Web client application│ └── documentation/ → Documentation site├── libs/│ ├── type-graphql-query/ → GraphQL schema builder│ ├── drizzle-query/ → Database query layer│ ├── db/ → Database schema│ └── ... → Other shared libraries└── packages/ → Shared packagesAPI Structure
Section intitulée « API Structure »The API (apps/api/src/) is organized into modules, where each module represents a domain entity or feature.
Module Structure
Section intitulée « Module Structure »Each module follows a consistent structure:
module-name/├── dto/ → Data Transfer Objects│ ├── module-name.type.ts → GraphQL output type│ ├── module-name.input.ts → GraphQL input type (optional)│ └── module-name-aggregate.type.ts → Aggregate type (optional)├── module-name.module.ts → Module configuration├── module-name.resolver.ts → Custom resolvers (optional)└── module-name.service.ts → Business logic (optional)Example: Product Module
Section intitulée « Example: Product Module »product/├── dto/│ ├── product.type.ts → Product GraphQL type│ ├── product.input.ts → Product input for mutations│ └── product-aggregate.type.ts → Product aggregation type└── product.module.ts → Product module configurationModule Types
Section intitulée « Module Types »1. Standard CRUD Modules
Section intitulée « 1. Standard CRUD Modules »Most modules are standard CRUD modules that use Type GraphQL Query to automatically generate resolvers.
Example: product.module.ts
import { Permission, TypeGraphqlQueryOptions } from '@lib/type-graphql-query';import { Product } from './dto/product.type';import { ProductInput } from './dto/product.input';import { product as productTable } from '@lib/db';
const resolvers = [ { enableSubscriptions: true, DTOClass: Product, UpsertDTOClass: ProductInput, EntityClass: productTable, enableTotalCount: true, enableAggregate: true, read: { authorize: Permission('product.view'), }, create: { authorize: Permission('product.create'), }, // ... other operations },] satisfies TypeGraphqlQueryOptions['resolvers'];
export default resolvers;2. Custom Resolver Modules
Section intitulée « 2. Custom Resolver Modules »Some modules have custom resolvers for complex business logic.
Example: user/user.resolver.ts
import { Resolver, Query } from '@lib/type-graphql';import { User } from './dto/user.type';
@Resolver(() => User)export class UserResolver { @Query(() => User) async me(@Ctx() context: Context): Promise<User> { // Custom logic }}3. Service Modules
Section intitulée « 3. Service Modules »Modules can have service classes for complex business logic.
Example: user/user.service.ts
export class UserService { async findUserById(id: string): Promise<User> { // Business logic }}DTO Structure
Section intitulée « DTO Structure »Type (Output)
Section intitulée « Type (Output) »GraphQL output types define what data is returned from queries.
import { ObjectType, ID } from '@lib/type-graphql';import { EnhancedField, IDField, Relation } from '@lib/type-graphql-query';
@ObjectType()export class Product { @IDField(() => ID) id: string;
@EnhancedField(() => String) name: string;
@Relation('category', () => Category) category: Category;}Input (Mutation Input)
Section intitulée « Input (Mutation Input) »Input types define what data is accepted for mutations.
import { InputType, Field } from '@lib/type-graphql';
@InputType()export class ProductInput { @Field(() => String) name: string;
@Field(() => String, { nullable: true }) description?: string;}Module Registration
Section intitulée « Module Registration »All modules are registered in create-schema.ts:
import productModuleResolvers from './product/product.module';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ ...productModuleResolvers, // ... other modules ], // ... other options});Directory Organization
Section intitulée « Directory Organization »Core Directories
Section intitulée « Core Directories »auth/- Authentication and authorizationacl/- Access control listsuser/- User managementorganization/- Organization management
Business Logic Directories
Section intitulée « Business Logic Directories »product/- Product managementorder/- Order managementorder-line/- Order line itemscategory/- Product categoriesstorage/- Stock management
Supporting Directories
Section intitulée « Supporting Directories »file/- File managementactivity/- Activity trackinggeolocalization/- Geolocation serviceslogging/- Logging systemmetrics/- Metrics collection
Utility Directories
Section intitulée « Utility Directories »utils/- Utility functionsservices/- Shared servicesdataloader/- DataLoader utilities
Naming Conventions
Section intitulée « Naming Conventions »- Modules:
module-name.module.ts - Resolvers:
module-name.resolver.ts - Services:
module-name.service.ts - Types:
module-name.type.ts - Inputs:
module-name.input.ts - Aggregates:
module-name-aggregate.type.ts
Directories
Section intitulée « Directories »- Use kebab-case for directory names:
order-line/,product-image/ - Use singular for module directories:
product/,order/,user/
- Use PascalCase for class names:
Product,UserResolver,OrderService
Variables and Functions
Section intitulée « Variables and Functions »- Use camelCase for variables and functions:
getUserById,productService
Import Conventions
Section intitulée « Import Conventions »Absolute Imports
Section intitulée « Absolute Imports »Use absolute imports with path aliases:
import { Product } from '../product/dto/product.type';import { db } from '../database';import { Permission } from '@lib/type-graphql-query';Import Order
Section intitulée « Import Order »- External libraries
- Internal libraries (
@lib/...) - Relative imports (
./...,../...)
Example:
import { ObjectType, ID } from '@lib/type-graphql';import { EnhancedField, IDField } from '@lib/type-graphql-query';import { product as productTable } from '@lib/db';import { Category } from '../category/dto/category.type';import { metadataColumns } from '../utils/metadata-columns';Creating a New Module
Section intitulée « Creating a New Module »Follow these steps to create a new module:
Step 1: Create Directory Structure
Section intitulée « Step 1: Create Directory Structure »mkdir -p apps/api/src/new-module/dtoStep 2: Create DTOs
Section intitulée « Step 2: Create DTOs »Create dto/new-module.type.ts:
import { ObjectType, ID } from '@lib/type-graphql';import { EnhancedField, IDField } from '@lib/type-graphql-query';
@ObjectType()export class NewModule { @IDField(() => ID) id: string;
@EnhancedField(() => String) name: string;}Create dto/new-module.input.ts:
import { InputType, Field } from '@lib/type-graphql';
@InputType()export class NewModuleInput { @Field(() => String) name: string;}Step 3: Create Module Configuration
Section intitulée « Step 3: Create Module Configuration »Create new-module.module.ts:
import { Permission, TypeGraphqlQueryOptions } from '@lib/type-graphql-query';import { NewModule } from './dto/new-module.type';import { NewModuleInput } from './dto/new-module.input';import { newModule as newModuleTable } from '@lib/db';
const resolvers = [ { DTOClass: NewModule, UpsertDTOClass: NewModuleInput, EntityClass: newModuleTable, read: { authorize: Permission('new-module.view'), }, create: { authorize: Permission('new-module.create'), }, update: { authorize: Permission('new-module.update'), }, delete: { authorize: Permission('new-module.delete'), }, },] satisfies TypeGraphqlQueryOptions['resolvers'];
export default resolvers;Step 4: Register Module
Section intitulée « Step 4: Register Module »Add to create-schema.ts:
import newModuleResolvers from './new-module/new-module.module';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ ...newModuleResolvers, // ... other modules ],});Best Practices
Section intitulée « Best Practices »1. Keep Modules Focused
Section intitulée « 1. Keep Modules Focused »Each module should represent a single domain entity or feature.
2. Use Type GraphQL Query
Section intitulée « 2. Use Type GraphQL Query »Prefer using Type GraphQL Query for standard CRUD operations instead of writing custom resolvers.
3. Separate Concerns
Section intitulée « 3. Separate Concerns »- DTOs - Data structure
- Modules - Configuration
- Resolvers - Custom GraphQL operations
- Services - Business logic
4. Reuse Common Patterns
Section intitulée « 4. Reuse Common Patterns »Use utilities like metadataColumns for common field patterns:
import metadataColumns from '../utils/metadata-columns';
create: { ...metadataColumns, authorize: Permission('product.create')}5. Document Complex Logic
Section intitulée « 5. Document Complex Logic »Add comments for complex business logic or non-obvious decisions.
Common Tasks
Section intitulée « Common Tasks »| Task | Documentation |
|---|---|
| Create a new module | Creating a New Module |
| Add a GraphQL query | Type GraphQL Query - Queries |
| Add authentication | API - Authentication |
| Understand permissions | API - Permissions |
| Debug an issue | Debugging Guide |
Next Steps
Section intitulée « Next Steps »Continue your learning journey:
- 📝 Code Style - Learn coding conventions
- 🛠️ Development Guide - Understand the development workflow
- 🎯 Type GraphQL Query - Master the schema builder
- 🧪 Testing Guide - Learn how to write tests
Related Topics
Section intitulée « Related Topics »- Quick Start - Get started quickly
- API Overview - Understand the API architecture
- Type GraphQL Query - Learn about the schema builder
- Development Workflow - How to develop features