All skills
Skillintermediate

Architecture Decision Guide

| Framework | Best For | Pros | Cons | |-----------|----------|------|------| | **NestJS** | Enterprise apps, microservices | TypeScript-first, dependency injection, excellent docs | Opinionated, steeper learning curve | | **Express** | Simple APIs, flexibility | Minimal, huge ecosystem, well-known | Manual structure, less opinionated | | **Fastify** | High performance APIs | Fast, schema validati

Claude Code Knowledge Pack7/10/2026

Overview

Architecture Decision Guide

Technology Selection Matrix

Backend Framework Selection

FrameworkBest ForProsCons
NestJSEnterprise apps, microservicesTypeScript-first, dependency injection, excellent docsOpinionated, steeper learning curve
ExpressSimple APIs, flexibilityMinimal, huge ecosystem, well-knownManual structure, less opinionated
FastifyHigh performance APIsFast, schema validation, pluginsSmaller ecosystem than Express
FastAPIPython APIs, ML integrationAuto-docs, type hints, fastPython ecosystem only
Go/GinHigh-performance servicesCompiled, concurrent, fastVerbose, less rapid development

Decision criteria:

  • Team expertise: Choose familiar stack
  • Performance needs: Go/Fastify for high throughput
  • Type safety: NestJS/FastAPI for TypeScript/Python
  • Flexibility: Express for custom architectures

Frontend Framework Selection

FrameworkBest ForProsCons
ReactMost use cases, large appsHuge ecosystem, flexible, well-supportedNot batteries-included, decision fatigue
VueProgressive enhancementGentle learning curve, good docs, reactiveSmaller ecosystem than React
AngularEnterprise appsComplete framework, TypeScript nativeHeavy, opinionated, steep curve
SveltePerformance-critical appsCompiled, no virtual DOM, small bundleSmaller ecosystem, fewer resources
Next.jsSSR/SSG apps, SEOReact + routing + SSR, excellent DXVercel-centric, complexity for simple apps

Decision criteria:

  • SEO requirements: Next.js/Nuxt for SSR
  • Team size: Angular for large teams, Vue for small
  • Ecosystem: React for maximum third-party support
  • Performance: Svelte for minimal bundle size

Database Selection

DatabaseBest ForProsCons
PostgreSQLRelational data, ACIDFeature-rich, reliable, JSON supportComplex queries can be slow
MySQLRead-heavy workloadsMature, fast reads, replicationLess feature-rich than Postgres
MongoDBFlexible schemas, rapid devSchema-less, horizontal scalingNo transactions (old versions)
RedisCaching, sessions, queuesExtremely fast, versatileIn-memory only, data structures limited
DynamoDBAWS serverless, high scaleManaged, predictable performanceVendor lock-in, query limitations

Decision criteria:

  • ACID requirements: PostgreSQL/MySQL
  • Flexible schemas: MongoDB
  • Caching layer: Redis (always)
  • AWS serverless: DynamoDB
  • Default choice: PostgreSQL (most versatile)

State Management (Frontend)

SolutionBest ForComplexityBundle Size
React ContextSimple state, few updatesLowNone (built-in)
ZustandMedium apps, simplicityLow1KB
Redux ToolkitComplex state, time-travel debugMedium15KB
Jotai/RecoilAtomic state, derived stateMedium3KB
MobXObservable state, OOP styleMedium16KB
TanStack QueryServer state onlyLow12KB

Decision criteria:

  • Simple app: Context or Zustand
  • Complex state logic: Redux Toolkit
  • Server state: TanStack Query (don't use global state)
  • Real-time apps: Zustand + WebSocket

Monolith vs Microservices

Decision Matrix

FactorMonolithMicroservices
Team size< 10 developers> 10 developers
System complexitySimple domainComplex, bounded contexts
DeploymentSimple, all-at-onceComplex, independent services
ScalingVertical scalingHorizontal per service
Development speedFast initiallySlower setup, faster iteration
InfrastructureSimpler (1 app, 1 DB)Complex (K8s, service mesh, multiple DBs)
Data consistencyACID transactionsEventual consistency, sagas
TestingEasier integration testsMore complex testing
MonitoringSingle app to monitorDistributed tracing needed

When to Use Monolith

✓ Starting new product (validate idea first)
✓ Small team (< 10 developers)
✓ Simple domain with few bounded contexts
✓ Need rapid development
✓ Limited infrastructure budget
✓ Straightforward deployment requirements

When to Use Microservices

✓ Large team (> 10 developers)
✓ Clear bounded contexts in domain
✓ Different services have different scaling needs
✓ Need independent deployment cycles
✓ Multiple teams working independently
✓ Polyglot requirements (different languages)
✓ Have DevOps expertise and infrastructure

Modular Monolith (Recommended Middle Ground)

// Structure monolith with clear boundaries
project/
├── src/
│   ├── modules/
│   │   ├── users/
│   │   │   ├── users.module.ts
│   │   │   ├── users.service.ts
│   │   │   ├── users.controller.ts
│   │   │   └── users.repository.ts
│   │   ├── orders/
│   │   │   ├── orders.module.ts
│   │   │   └── ...
│   │   └── payments/
│   │       └── ...
│   └── shared/
│       ├── database/
│       └── auth/

// Clear module boundaries, can split later if needed

API Architecture Patterns

REST vs GraphQL

AspectRESTGraphQL
Best forCRUD operations, public APIsComplex queries, mobile apps
Learning curveLowMedium-high
Over-fetchingCommon issueSolved by design
Under-fetchingRequires multiple requestsSingle request
CachingHTTP caching works wellMore complex caching
VersioningURL versioning (/v1, /v2)Schema evolution
ToolingSwagger, PostmanGraphiQL, Apollo Studio

Choose REST when:

  • Building simple CRUD APIs
  • Need HTTP caching
  • Public API with many consumers
  • Team unfamiliar with GraphQL

Choose GraphQL when:

  • Mobile apps need flexible queries
  • Complex data requirements
  • Rapid frontend iteration
  • Real-time subscriptions needed

BFF Pattern (Backend for Frontend)

// Use when frontend needs differ from backend APIs
// Mobile BFF: Returns minimal data, optimized responses
@Controller('mobile-bff')

  @Get('dashboard')
  async getMobileDashboard(@CurrentUser() user: User) {
    const [profile, notifications] = await Promise.all([
      this.userService.getProfile(user.id),
      this.notificationService.getUnread(user.id, 5), // Only 5 for mobile
    ]);
    return { profile, notifications }; // Minimal payload
  }
}

// Web BFF: Returns richer data
@Controller('web-bff')

  @Get('dashboard')
  async getWebDashboard(@CurrentUser() user: User) {
    const [profile, notifications, analytics, recentActivity] = await Promise.all([
      this.userService.getProfile(user.id),
      this.notificationService.getUnread(user.id, 20), // More for web
      this.analyticsService.getUserStats(user.id),
      this.activityService.getRecent(user.id),
    ]);
    return { profile, notifications, analytics, recentActivity };
  }
}

Authentication Strategy

JWT vs Session-based

AspectJWTSession
ScalabilityStateless, horizontal scalingRequires session store
PerformanceNo DB lookup per requestDB/Redis lookup needed
RevocationComplex (requires blacklist)Simple (delete session)
SecurityToken can't be invalidatedEasy to invalidate
Mobile/SPAIdeal for token storageRequires cookies
MicroservicesEasy to share across servicesHarder to share

Hybrid approach (Recommended):

// Short-lived access token (15min) + refresh token (7 days)
interface AuthTokens {
  accessToken: string;   // JWT, 15 minutes, stored in memory
  refreshToken: string;  // Opaque token, 7 days, httpOnly cookie
}

// Access token: Stateless, fast validation
// Refresh token: Stored in DB, can be revoked

SSO Integration Options

ProviderUse CaseComplexity
OAuth2/OIDCStandard protocol, most IdPsMedium
SAMLEnterprise customers, legacyHigh
Social loginsB2C apps (Google, GitHub)Low
Auth0/OktaManaged solution, rapid setupLow

Caching Strategy

Layered Caching Approach

// Layer 1: CDN caching (static assets)
// CloudFront, Cloudflare

// Layer 2: API response caching (Redis)
const cacheKey = `user:${userId}:profile`;
let profile = await redis.get(cacheKey);

if (!profile) {
  profile = await db.users.findById(userId);
  await redis.setex(cacheKey, 300, JSON.stringify(profile)); // 5 min TTL
}

// Layer 3: Database query caching
// PostgreSQL prepared statements, query plan caching

// Layer 4: Application-level caching
const userCache = new LRU({ max: 1000 });

Cache Invalidation Patterns

// Write-through: Update cache on write
async updateUser(id: string, data: UpdateUserDto) {
  const user = await db.users.update(id, data);
  await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 300);
  return user;
}

// Write-behind: Invalidate cache, lazy load
async updateUser(id: string, data: UpdateUserDto) {
  const user = await db.users.update(id, data);
  await redis.del(`user:${id}`); // Delete, will reload on next read
  return user;
}

// Event-based: Invalidate related caches
eventBus.on('user.updated', async ({ userId }) => {
  await Promise.all([
    redis.del(`user:${userId}`),
    redis.del(`user:${userId}:posts`),
    redis.del(`user:${userId}:followers`),
  ]);
});

Deployment Strategy

Environment Progression

Development → Staging → Production

Development:
- Local dev servers
- Docker Compose for dependencies
- Hot reload enabled
- Debug logging
- Relaxed security

Staging:
- Production-like environment
- Real integrations (test mode)
- E2E tests run here
- Performance testing
- Security scanning

Production:
- High availability setup
- Blue-green deployment
- Monitoring & alerting
- Automated rollback
- Strict security

Deployment Patterns

PatternDowntimeRollbackComplexityUse When
RecreateYesManualLowDev/staging only
RollingNoGradualMediumStandard deployments
Blue-GreenNoInstantMediumZero-downtime required
CanaryNoGradualHighHigh-risk changes
A/B TestingNoGradualHighFeature validation

Quick Decision Trees

"Which database should I use?"

Need ACID transactions? → PostgreSQL
NoSQL with flexible schema? → MongoDB
Caching/sessions/queues? → Redis
AWS serverless? → DynamoDB
High read throughput? → PostgreSQL + read replicas

"Monolith or microservices?"

New product? → Modular monolith
Team < 10 people? → Modular monolith
Clear bounded contexts? → Consider microservices
Different scaling needs? → Microservices
Limited DevOps resources? → Monolith

"REST or GraphQL?"

Simple CRUD? → REST
Mobile app with flexible queries? → GraphQL
Public API? → REST
Complex data requirements? → GraphQL
Team knows GraphQL? → GraphQL, otherwise REST

"Which state management?"

Simple app, few global state? → React Context
Server state (API data)? → TanStack Query
Medium complexity? → Zustand
Complex state logic? → Redux Toolkit
Real-time updates? → Zustand + WebSocket