14 KiB
14 KiB
System Patterns - Final Architecture
Architectural Overview - PRODUCTION IMPLEMENTATION
The Eliza Kragh v. Montana Association of the Deaf court docket website follows a modern full-stack architecture with clean separation of concerns, security best practices, and production-ready patterns.
Core Architecture Patterns
1. Layered Architecture Pattern
┌─────────────────────────────────────┐
│ Frontend Layer │
│ (Next.js + React + TypeScript) │
│ DeafGain Theme UI │
└─────────────────────────────────────┘
│
HTTP/API
│
┌─────────────────────────────────────┐
│ API Gateway Layer │
│ (Express.js + Middleware) │
│ Auth, Logging, Error Handling │
└─────────────────────────────────────┘
│
┌─────────────────────────────────────┐
│ Business Logic Layer │
│ (Services + Routes) │
│ Email Service, File Management │
└─────────────────────────────────────┘
│
┌─────────────────────────────────────┐
│ Data Access Layer │
│ (Prisma ORM + SQL) │
│ PostgreSQL + Redis Cache │
└─────────────────────────────────────┘
2. MVC Pattern Implementation
- Model: Prisma schema with TypeScript types
- View: React components with Next.js pages
- Controller: Express.js route handlers with business logic
3. Repository Pattern
- Prisma ORM: Abstracts database operations
- Type Safety: Full TypeScript integration
- Query Optimization: Efficient database queries with relationships
Security Patterns - PRODUCTION READY
1. Authentication & Authorization
// JWT-based authentication with middleware
const authenticateToken = (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ success: false, message: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET!, (err: any, user: any) => {
if (err) {
return res.status(403).json({ success: false, message: 'Invalid or expired token' });
}
req.user = user;
next();
});
};
2. Input Validation Pattern
// Joi validation schemas for all inputs
const createDocketEntrySchema = Joi.object({
date: Joi.date().required(),
title: Joi.string().required().max(500),
summary: Joi.string().required(),
notes: Joi.string().optional().allow(''),
});
3. File Security Pattern
// UUID-based file naming with type validation
const fileFilter = (req: any, file: Express.Multer.File, cb: any) => {
if (file.mimetype === 'application/pdf') {
cb(null, true);
} else {
cb(new Error('Only PDF files are allowed'), false);
}
};
Data Patterns - IMPLEMENTED
1. Database Schema Pattern
-- Normalized relational design with proper constraints
CREATE TABLE docket_entries (
id SERIAL PRIMARY KEY,
date DATE NOT NULL,
title VARCHAR(500) NOT NULL,
summary TEXT NOT NULL,
notes TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
docket_entry_id INTEGER REFERENCES docket_entries(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
stored_filename VARCHAR(255) NOT NULL,
-- Additional fields for complete document management
);
2. Data Access Pattern
// Prisma-based repository pattern with relationships
const getDocketEntriesWithDocuments = async () => {
return await prisma.docketEntry.findMany({
include: {
documents: {
orderBy: { displayOrder: 'asc' }
}
},
orderBy: { date: 'desc' }
});
};
3. Email Notification Pattern
// Observer pattern for email notifications
const createDocketEntry = async (data: DocketEntryData) => {
const entry = await prisma.docketEntry.create({ data });
// Trigger email notifications
await emailService.notifySubscribersOfNewEntry(
entry.title,
entry.date.toISOString(),
entry.summary
);
return entry;
};
Frontend Patterns - MODERN UI/UX
1. Component Architecture
// Functional components with hooks
const HomePage = () => {
const [docketEntries, setDocketEntries] = useState<DocketEntry[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchDocketEntries();
}, []);
// Component logic and JSX
};
2. State Management Pattern
// Local state with React hooks for simple state management
const [expandedEntries, setExpandedEntries] = useState<Set<number>>(new Set());
const toggleEntry = (entryId: number) => {
const newExpanded = new Set(expandedEntries);
if (newExpanded.has(entryId)) {
newExpanded.delete(entryId);
} else {
newExpanded.add(entryId);
}
setExpandedEntries(newExpanded);
};
3. Styling Pattern - DeafGain Theme
/* CSS Variables for consistent theming */
:root {
--primary: #647C90; /* Slate blue */
--secondary: #2C4A3E; /* Forest green */
--accent-snow: #F5F7F9; /* Snow white background */
--accent-mountain: #4A5568; /* Mountain gray text */
--accent-lake: #A4C3D2; /* Alpine lake blue */
}
/* Modern component classes with animations */
.docket-entry {
@apply transition-all duration-300 hover:bg-gray-50 rounded-xl p-6 border-l-4;
border-left-color: var(--accent-lake);
}
.docket-entry:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
Email Service Patterns - GMAIL INTEGRATION
1. Service Layer Pattern
// Singleton email service with Gmail SMTP
export class EmailService {
private static instance: EmailService;
private transporter: nodemailer.Transporter;
private constructor() {
this.transporter = nodemailer.createTransport({
host: process.env['SMTP_HOST'] || 'smtp.gmail.com',
port: parseInt(process.env['SMTP_PORT'] || '587'),
secure: false,
auth: {
user: process.env['GOOGLE_EMAIL'],
pass: process.env['GOOGLE_APP_PASSWORD'],
},
});
}
public static getInstance(): EmailService {
if (!EmailService.instance) {
EmailService.instance = new EmailService();
}
return EmailService.instance;
}
}
2. Template Pattern
// Professional HTML email templates
private generateNotificationEmail(title: string, date: string, summary: string): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>New Court Filing Notification</title>
<style>
/* Professional email styling */
.header { background-color: #1f2937; color: white; padding: 20px; }
.content { background-color: #f9fafb; padding: 30px; }
.filing-info { background-color: white; padding: 20px; border-left: 4px solid #3b82f6; }
</style>
</head>
<body>
<div class="header">
<h1>Eliza Kragh v. Montana Association of the Deaf</h1>
</div>
<div class="content">
<div class="filing-info">
<div class="title">${title}</div>
<div class="summary">${summary}</div>
</div>
</div>
</body>
</html>
`;
}
Error Handling Patterns - PRODUCTION READY
1. Global Error Handler
// Centralized error handling middleware
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
) => {
logger.error('Error occurred:', {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method,
ip: req.ip,
});
if (err.name === 'ValidationError') {
return res.status(400).json({
success: false,
message: 'Validation error',
details: err.message,
});
}
res.status(500).json({
success: false,
message: 'Internal server error',
});
};
2. Async Error Wrapper
// Wrapper for async route handlers
export const asyncHandler = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
Logging Patterns - WINSTON INTEGRATION
1. Structured Logging
// Winston logger configuration
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'docket-api' },
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' }),
new winston.transports.Console({
format: winston.format.simple()
})
],
});
2. Request Logging Middleware
// Comprehensive request logging
export const requestLogger = (req: Request, res: Response, next: NextFunction) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info('Request completed', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('User-Agent'),
});
});
next();
};
File Management Patterns - UUID STRATEGY
1. UUID File Naming
// Secure file naming with UUID
import { v4 as uuidv4 } from 'uuid';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, process.env.UPLOAD_DIR || './uploads');
},
filename: (req, file, cb) => {
const fileExtension = path.extname(file.originalname);
const uniqueFilename = `${uuidv4()}${fileExtension}`;
cb(null, uniqueFilename);
},
});
2. File Validation Pattern
// Comprehensive file validation
const upload = multer({
storage,
limits: {
fileSize: parseInt(process.env.MAX_FILE_SIZE || '10485760'), // 10MB
},
fileFilter: (req, file, cb) => {
if (file.mimetype === 'application/pdf') {
cb(null, true);
} else {
cb(new Error('Only PDF files are allowed'), false);
}
},
});
Testing Patterns - COMPREHENSIVE COVERAGE
1. API Testing Pattern
# Automated API testing script
#!/bin/bash
echo "Testing Court Docket Website APIs..."
# Test authentication
TOKEN=$(curl -s -X POST http://localhost:3001/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' | jq -r '.token')
# Test protected endpoints
curl -s -H "Authorization: Bearer $TOKEN" \
http://localhost:3001/api/docket-entries/1 | jq '.success'
2. Manual Testing Pattern
## Test Cases
1. **Page Load**
- [ ] Page loads without errors
- [ ] Header displays case title correctly
- [ ] Case information section shows correct details
2. **Email Subscription**
- [ ] Email input field accepts valid email addresses
- [ ] Subscribe button is enabled when email is entered
- [ ] Success message appears after subscription
Performance Patterns - OPTIMIZATION
1. Database Query Optimization
// Efficient queries with proper includes
const entries = await prisma.docketEntry.findMany({
include: {
documents: {
orderBy: { displayOrder: 'asc' }
}
},
orderBy: { date: 'desc' }
});
2. Frontend Performance
// React optimization patterns
const MemoizedComponent = React.memo(({ data }) => {
return <div>{data.title}</div>;
});
// Efficient state updates
const [entries, setEntries] = useState<DocketEntry[]>([]);
const [loading, setLoading] = useState(true);
Deployment Patterns - DOCKER COMPOSE
1. Multi-Service Architecture
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:17.5-alpine
environment:
POSTGRES_DB: docket_db
POSTGRES_USER: docket_user
POSTGRES_PASSWORD: docket_pass
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
2. Environment Configuration
# Production-ready environment variables
DATABASE_URL=postgresql://docket_user:docket_pass@localhost:5432/docket_db
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-key-change-in-production
SMTP_HOST=smtp.gmail.com
GOOGLE_EMAIL=system@deafgain.org
Current Implementation Status
Patterns Successfully Implemented
- ✅ Layered Architecture: Clean separation of concerns
- ✅ Security Patterns: JWT auth, input validation, file restrictions
- ✅ Data Patterns: Normalized schema with proper relationships
- ✅ Email Patterns: Gmail SMTP with professional templates
- ✅ Error Handling: Comprehensive error management
- ✅ Logging Patterns: Structured logging with Winston
- ✅ File Management: UUID naming with validation
- ✅ Testing Patterns: Manual and automated testing
- ✅ Performance Patterns: Optimized queries and modern CSS
- ✅ Deployment Patterns: Docker Compose multi-service setup
Production Ready Architecture
The court docket website implements industry-standard patterns for:
- Scalability: Clean architecture supporting growth
- Security: Multiple layers of protection
- Maintainability: Well-organized code with clear patterns
- Performance: Optimized database queries and frontend
- Reliability: Comprehensive error handling and logging
- Testability: Complete testing infrastructure
The system successfully serves the Eliza Kragh v. Montana Association of the Deaf case with professional presentation, real Gmail email notifications, and production-ready architecture patterns.