# Complete Examples - Full Working Code Real-world examples showing complete implementation patterns. ## Table of Contents - [Complete Controller Example](#complete-controller-example) - [Complete Service with DI](#complete-service-with-di) - [Complete Route File](#complete-route-file) - [Complete Repository](#complete-repository) - [Refactoring Example: Bad to Good](#refactoring-example-bad-to-good) - [End-to-End Feature Example](#end-to-end-feature-example) --- ## Complete Controller Example ### UserController (Following All Best Practices) ```typescript // controllers/UserController.ts import { Request, Response } from 'express'; import { BaseController } from './BaseController'; import { UserService } from '../services/userService'; import { createUserSchema, updateUserSchema } from '../validators/userSchemas'; import { z } from 'zod'; export class UserController extends BaseController { private userService: UserService; constructor() { super(); this.userService = new UserService(); } async getUser(req: Request, res: Response): Promise { try { this.addBreadcrumb('Fetching user', 'user_controller', { userId: req.params.id, }); const user = await this.withTransaction( 'user.get', 'db.query', () => this.userService.findById(req.params.id) ); if (!user) { return this.handleError( new Error('User not found'), res, 'getUser', 404 ); } this.handleSuccess(res, user); } catch (error) { this.handleError(error, res, 'getUser'); } } async listUsers(req: Request, res: Response): Promise { try { const users = await this.userService.getAll(); this.handleSuccess(res, users); } catch (error) { this.handleError(error, res, 'listUsers'); } } async createUser(req: Request, res: Response): Promise { try { // Validate input with Zod const validated = createUserSchema.parse(req.body); // Track performance const user = await this.withTransaction( 'user.create', 'db.mutation', () => this.userService.create(validated) ); this.handleSuccess(res, user, 'User created successfully', 201); } catch (error) { if (error instanceof z.ZodError) { return this.handleError(error, res, 'createUser', 400); } this.handleError(error, res, 'createUser'); } } async updateUser(req: Request, res: Response): Promise { try { const validated = updateUserSchema.parse(req.body); const user = await this.userService.update( req.params.id, validated ); this.handleSuccess(res, user, 'User updated'); } catch (error) { if (error instanceof z.ZodError) { return this.handleError(error, res, 'updateUser', 400); } this.handleError(error, res, 'updateUser'); } } async deleteUser(req: Request, res: Response): Promise { try { await this.userService.delete(req.params.id); this.handleSuccess(res, null, 'User deleted', 204); } catch (error) { this.handleError(error, res, 'deleteUser'); } } } ``` --- ## Complete Service with DI ### UserService ```typescript // services/userService.ts import { UserRepository } from '../repositories/UserRepository'; import { ConflictError, NotFoundError, ValidationError } from '../types/errors'; import type { CreateUserDTO, UpdateUserDTO, User } from '../types/user.types'; export class UserService { private userRepository: UserRepository; constructor(userRepository?: UserRepository) { this.userRepository = userRepository || new UserRepository(); } async findById(id: string): Promise { return await this.userRepository.findById(id); } async getAll(): Promise { return await this.userRepository.findActive(); } async create(data: CreateUserDTO): Promise { // Business rule: validate age if (data.age < 18) { throw new ValidationError('User must be 18 or older'); } // Business rule: check email uniqueness const existing = await this.userRepository.findByEmail(data.email); if (existing) { throw new ConflictError('Email already in use'); } // Create user with profile return await this.userRepository.create({ email: data.email, profile: { create: { firstName: data.firstName, lastName: data.lastName, age: data.age, }, }, }); } async update(id: string, data: UpdateUserDTO): Promise { // Check exists const existing = await this.userRepository.findById(id); if (!existing) { throw new NotFoundError('User not found'); } // Business rule: email uniqueness if changing if (data.email && data.email !== existing.email) { const emailTaken = await this.userRepository.findByEmail(data.email); if (emailTaken) { throw new ConflictError('Email already in use'); } } return await this.userRepository.update(id, data); } async delete(id: string): Promise { const existing = await this.userRepository.findById(id); if (!existing) { throw new NotFoundError('User not found'); } await this.userRepository.delete(id); } } ``` --- ## Complete Route File ### userRoutes.ts ```typescript // routes/userRoutes.ts import { Router } from 'express'; import { UserController } from '../controllers/UserController'; import { SSOMiddlewareClient } from '../middleware/SSOMiddleware'; import { auditMiddleware } from '../middleware/auditMiddleware'; const router = Router(); const controller = new UserController(); // GET /users - List all users router.get('/', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.listUsers(req, res) ); // GET /users/:id - Get single user router.get('/:id', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.getUser(req, res) ); // POST /users - Create user router.post('/', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.createUser(req, res) ); // PUT /users/:id - Update user router.put('/:id', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.updateUser(req, res) ); // DELETE /users/:id - Delete user router.delete('/:id', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.deleteUser(req, res) ); export default router; ``` --- ## Complete Repository ### UserRepository ```typescript // repositories/UserRepository.ts import { PrismaService } from '@project-lifecycle-portal/database'; import type { User, Prisma } from '@prisma/client'; export class UserRepository { async findById(id: string): Promise { return PrismaService.main.user.findUnique({ where: { id }, include: { profile: true }, }); } async findByEmail(email: string): Promise { return PrismaService.main.user.findUnique({ where: { email }, include: { profile: true }, }); } async findActive(): Promise { return PrismaService.main.user.findMany({ where: { isActive: true }, include: { profile: true }, orderBy: { createdAt: 'desc' }, }); } async create(data: Prisma.UserCreateInput): Promise { return PrismaService.main.user.create({ data, include: { profile: true }, }); } async update(id: string, data: Prisma.UserUpdateInput): Promise { return PrismaService.main.user.update({ where: { id }, data, include: { profile: true }, }); } async delete(id: string): Promise { // Soft delete return PrismaService.main.user.update({ where: { id }, data: { isActive: false, deletedAt: new Date(), }, }); } } ``` --- ## Refactoring Example: Bad to Good ### BEFORE: Business Logic in Routes ❌ ```typescript // routes/postRoutes.ts (BAD - 200+ lines) router.post('/posts', async (req, res) => { try { const username = res.locals.claims.preferred_username; const responses = req.body.responses; const stepInstanceId = req.body.stepInstanceId; // ❌ Permission check in route const userId = await userProfileService.getProfileByEmail(username).then(p => p.id); const canComplete = await permissionService.canCompleteStep(userId, stepInstanceId); if (!canComplete) { return res.status(403).json({ error: 'No permission' }); } // ❌ Business logic in route const post = await postRepository.create({ title: req.body.title, content: req.body.content, authorId: userId }); // ❌ More business logic... if (res.locals.isImpersonating) { impersonationContextStore.storeContext(...); } // ... 100+ more lines res.json({ success: true, data: result }); } catch (e) { handler.handleException(res, e); } }); ``` ### AFTER: Clean Separation ✅ **1. Clean Route:** ```typescript // routes/postRoutes.ts import { PostController } from '../controllers/PostController'; const router = Router(); const controller = new PostController(); // ✅ CLEAN: 8 lines total! router.post('/', SSOMiddlewareClient.verifyLoginStatus, auditMiddleware, async (req, res) => controller.createPost(req, res) ); export default router; ``` **2. Controller:** ```typescript // controllers/PostController.ts export class PostController extends BaseController { private postService: PostService; constructor() { super(); this.postService = new PostService(); } async createPost(req: Request, res: Response): Promise { try { const validated = createPostSchema.parse({ ...req.body, }); const result = await this.postService.createPost( validated, res.locals.userId ); this.handleSuccess(res, result, 'Post created successfully'); } catch (error) { this.handleError(error, res, 'createPost'); } } } ``` **3. Service:** ```typescript // services/postService.ts export class PostService { async createPost( data: CreatePostDTO, userId: string ): Promise { // Permission check const canComplete = await permissionService.canCompleteStep( userId, data.stepInstanceId ); if (!canComplete) { throw new ForbiddenError('No permission to complete step'); } // Execute workflow const engine = await createWorkflowEngine(); const command = new CompleteStepCommand( data.stepInstanceId, userId, data.responses ); const events = await engine.executeCommand(command); // Handle impersonation if (context.isImpersonating) { await this.handleImpersonation(data.stepInstanceId, context); } return { events, success: true }; } private async handleImpersonation(stepInstanceId: number, context: any) { impersonationContextStore.storeContext(stepInstanceId, { originalUserId: context.originalUserId, effectiveUserId: context.effectiveUserId, }); } } ``` **Result:** - Route: 8 lines (was 200+) - Controller: 25 lines - Service: 40 lines - **Testable, maintainable, reusable!** --- ## End-to-End Feature Example ### Complete User Management Feature **1. Types:** ```typescript // types/user.types.ts export interface User { id: string; email: string; isActive: boolean; profile?: UserProfile; } export interface CreateUserDTO { email: string; firstName: string; lastName: string; age: number; } export interface UpdateUserDTO { email?: string; firstName?: string; lastName?: string; } ``` **2. Validators:** ```typescript // validators/userSchemas.ts import { z } from 'zod'; export const createUserSchema = z.object({ email: z.string().email(), firstName: z.string().min(1).max(100), lastName: z.string().min(1).max(100), age: z.number().int().min(18).max(120), }); export const updateUserSchema = z.object({ email: z.string().email().optional(), firstName: z.string().min(1).max(100).optional(), lastName: z.string().min(1).max(100).optional(), }); ``` **3. Repository:** ```typescript // repositories/UserRepository.ts export class UserRepository { async findById(id: string): Promise { return PrismaService.main.user.findUnique({ where: { id }, include: { profile: true }, }); } async create(data: Prisma.UserCreateInput): Promise { return PrismaService.main.user.create({ data, include: { profile: true }, }); } } ``` **4. Service:** ```typescript // services/userService.ts export class UserService { private userRepository: UserRepository; constructor() { this.userRepository = new UserRepository(); } async create(data: CreateUserDTO): Promise { const existing = await this.userRepository.findByEmail(data.email); if (existing) { throw new ConflictError('Email already exists'); } return await this.userRepository.create({ email: data.email, profile: { create: { firstName: data.firstName, lastName: data.lastName, age: data.age, }, }, }); } } ``` **5. Controller:** ```typescript // controllers/UserController.ts export class UserController extends BaseController { private userService: UserService; constructor() { super(); this.userService = new UserService(); } async createUser(req: Request, res: Response): Promise { try { const validated = createUserSchema.parse(req.body); const user = await this.userService.create(validated); this.handleSuccess(res, user, 'User created', 201); } catch (error) { this.handleError(error, res, 'createUser'); } } } ``` **6. Routes:** ```typescript // routes/userRoutes.ts const router = Router(); const controller = new UserController(); router.post('/', SSOMiddlewareClient.verifyLoginStatus, async (req, res) => controller.createUser(req, res) ); export default router; ``` **7. Register in app.ts:** ```typescript // app.ts import userRoutes from './routes/userRoutes'; app.use('/api/users', userRoutes); ``` **Complete Request Flow:** ``` POST /api/users ↓ userRoutes matches / ↓ SSOMiddleware authenticates ↓ controller.createUser called ↓ Validates with Zod ↓ userService.create called ↓ Checks business rules ↓ userRepository.create called ↓ Prisma creates user ↓ Returns up the chain ↓ Controller formats response ↓ 200/201 sent to client ``` --- **Related Files:** - SKILL.md - [routing-and-controllers.md](routing-and-controllers.md) - [services-and-repositories.md](services-and-repositories.md) - [validation-patterns.md](validation-patterns.md)