惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
Cyberwarzone
Cyberwarzone
Schneier on Security
Schneier on Security
I
Intezer
Cisco Talos Blog
Cisco Talos Blog
Cloudbric
Cloudbric
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
博客园 - Franky
F
Fortinet All Blogs
Google DeepMind News
Google DeepMind News
Jina AI
Jina AI
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Full Disclosure
T
Troy Hunt's Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Project Zero
Project Zero
P
Palo Alto Networks Blog
Recorded Future
Recorded Future
美团技术团队
D
Docker
PCI Perspectives
PCI Perspectives
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 最新话题
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
人人都是产品经理
人人都是产品经理
月光博客
月光博客
D
DataBreaches.Net
The Hacker News
The Hacker News
爱范儿
爱范儿
V
Visual Studio Blog
Engineering at Meta
Engineering at Meta
SecWiki News
SecWiki News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
A
About on SuperTechFans
Latest news
Latest news
GbyAI
GbyAI
T
Tor Project blog
L
LINUX DO - 热门话题
Security Latest
Security Latest
博客园 - 聂微东
Y
Y Combinator Blog
AI
AI
M
MIT News - Artificial intelligence

博客园 - Zhentiw

[Skill] Frontend Design Skill Claude Code Hooks: Complete Guide [GenAI] Pre-retieval overview [GenAI] About Indexing [GenAI] Indexing overview [Vibe Coding] 降低大模型幻觉 - 重试机制 [Vibe coding] 降低大模型幻觉 - JSON 安全输出提示词 [Node.js] WebSocket基础知识 [LangGraph] 应用结构 [LangGrpah] Unit testing [LangGraph] Functional API [LangGraph] 中断注意事项 [LangGraph] 中断相关细节 [LangGrpah] 静态断点 [LangGrpah] 非阻塞式中断 [LangGraph] 阻塞式中断 [LangGraph] 语义搜索 [LangGraph] 长期记忆 [LangGraph] 管理短期记忆 [LangGraph] 自定义checkpointer [LangGraph] 短期记忆 [LangGraph] 时间旅行 [LangGraph] checkpoint常用API [LangGraph] 元数据标记 [LangGraph] 流 [Vitest] mockClear, mockReset, mockRestore [LangGraph] 将子图添加为节点
[GenAI] Migration Plan: Flat API → Layered Architecture
Zhentiw · 2026-07-14 · via 博客园 - Zhentiw

Executive Summary

This document outlines a five-phase migration from a monolithic Express API (all logic in one file) to a clean layered architecture with separate concerns: HTTP routes, business services, and data access repositories.

Current State: src/routes.ts contains ~150 lines mixing HTTP handling, validation, and database access.

Target State: Clean layered architecture with routes → services → repositories → database.

Migration Time: 2-3 hours (depending on AI agent efficiency)

Risk Level: Medium (can be rolled back at each phase boundary)


Phase 0: Current State Analysis

src/routes.ts Problems

  • Business logic mixed with HTTP request/response handling
  • Validation scattered throughout handlers
  • Direct database calls in route handlers
  • No service layer for code reuse
  • No repository layer for data access abstraction
  • Hard to test business logic (need to mock Express req/res)
  • Hard to reuse business logic in other contexts (CLI, batch jobs, etc.)

Pain Points

  1. Route handlers are 40+ lines each (should be 5-10 lines)
  2. Database access is hardcoded (no abstraction)
  3. Validation logic is duplicated across routes
  4. Error handling is inconsistent
  5. Testing requires mocking Express objects

Phase 1: Scaffold New Architecture

Goal: Create file structure and type definitions. No implementation yet.

Files to Create

src/refactored/types.ts — Shared type definitions

export class AppError extends Error {
  constructor(public code: string, message: string, public statusCode: number = 500) {
    super(message);
  }
}

export class ValidationError extends AppError {
  constructor(message: string) {
    super('VALIDATION_ERROR', message, 400);
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super('NOT_FOUND', `${resource} ${id} not found`, 404);
  }
}

export interface ServiceResult<T> {
  data: T | null;
  error: AppError | null;
}

src/refactored/repositories/ — Data access layer

  • user-repository.ts — User data access
  • order-repository.ts — Order data access

src/refactored/services/ — Business logic layer

  • user-service.ts — User business logic
  • order-service.ts — Order business logic

src/refactored/routes/ — HTTP handlers

  • user-routes.ts — User endpoints
  • order-routes.ts — Order endpoints

Commit

git commit -m "phase-1: scaffold new architecture with types and directory structure"

Goal: Create data access abstraction. Move all database queries to repositories.

UserRepository Interface

interface UserRepository {
  findAll(): Promise<User[]>;
  findById(id: string): Promise<User | null>;
  create(data: Omit<User, 'id'>): Promise<User>;
  update(id: string, data: Partial<User>): Promise<User>;
  delete(id: string): Promise<void>;
}

OrderRepository Interface

Similar pattern for orders.

Implementation Details

  • Repositories contain all database queries
  • Repositories are responsible for error handling from database
  • Repositories return domain objects (User, Order), not database rows
  • No HTTP logic in repositories
  • No validation logic in repositories

What Stays the Same

  • src/routes.ts still has the old handlers
  • Old code continues to work
  • No breaking changes yet

Commit

git commit -m "phase-2: extract repository layer for data access"

Goal: Move business logic from routes into services.

UserService Interface

interface UserService {
  getAllUsers(): Promise<User[]>;
  getUserById(id: string): Promise<ServiceResult<User>>;
  createUser(name: string, email: string): Promise<ServiceResult<User>>;
  updateUser(id: string, name: string, email: string): Promise<ServiceResult<User>>;
  deleteUser(id: string): Promise<ServiceResult<void>>;
}

OrderService Interface

Similar pattern for orders.

Service Responsibilities

  • Validation (throw ValidationError if invalid)
  • Business logic (calculations, orchestration)
  • Using repositories to access data
  • NOT responsible for HTTP response formatting

Services Throw Custom Errors

  • ValidationError for invalid input
  • NotFoundError for missing resources
  • Routes catch these and convert to HTTP responses

What Stays the Same

  • Old routes continue to work
  • Can have dual-mode (new services + old routes for now)

Commits

git commit -m "phase-2a: user repository implementation"
git commit -m "phase-2b: order repository implementation"
git commit -m "phase-3a: user service implementation"
git commit -m "phase-3b: order service implementation"

Phase 4: Test the New Architecture

Goal: Comprehensive test coverage for services and routes.

Unit Tests: Services (mock repositories)

tests/unit/services/
  user-service.test.ts
  order-service.test.ts

Test cases:

  • Happy path (success cases)
  • Validation errors (invalid input)
  • Not found errors (missing resources)
  • Edge cases (empty lists, boundary values)

Integration Tests: Routes (mock services)

tests/integration/routes/
  user-routes.test.ts
  order-routes.test.ts

Test cases:

  • Happy path HTTP responses
  • Error responses (400, 404, 500)
  • Request/response validation
  • Integration between route and service

Coverage Target

  • 80%+ line coverage
  • 90%+ critical path coverage
  • All error cases covered

Commits

git commit -m "phase-4a: unit tests for services"
git commit -m "phase-4b: integration tests for routes"

Phase 5: Migrate Routes to New Architecture

Goal: Update routes to use new services, remove old code.

What Changes

  • Routes are thin wrappers: parse request → call service → format response
  • Routes delegate all logic to services
  • Routes catch AppError and convert to HTTP responses

Old Code Removal

  • Delete old validation logic from routes
  • Delete old database queries from routes
  • Delete old error handling from routes
  • src/routes.ts becomes 80+ lines shorter

Breaking Changes

  • Old code is removed
  • Any code depending on the old patterns breaks (intentional)
  • All tests must still pass (new tests protect new code)

Final Verification

npm test        # All tests pass
npm run build   # Clean build, no errors
npm run lint    # No warnings
git log         # Five clean commits

Commit

git commit -m "phase-5: migrate routes to service layer and remove old code"

Rollback Strategy

If a phase fails:

  1. Phase 1 fails (scaffold) → Delete src/refactored/; re-plan
  2. Phase 2 fails (repositories)git revert [phase-2-commit]; fix types
  3. Phase 3 fails (services)git revert [phase-3-commit]; fix architecture
  4. Phase 4 fails (tests) → Don't commit; fix tests
  5. Phase 5 fails (integration)git revert [phase-5-commit]; fix routes

Always commit after each phase so rollback is clean.


Success Criteria

After Phase 5:

  • All tests pass (unit + integration)
  • No compilation errors or warnings
  • No linting warnings
  • Old code completely removed
  • No dead code or unused imports
  • Routes are simple and readable (each handler < 10 lines)
  • Services encapsulate all business logic
  • Repositories encapsulate all data access
  • Code review is straightforward (follows the plan)

Timeline

  • Phase 1 (Scaffold): 15 minutes
  • Phase 2 (Repositories): 30 minutes
  • Phase 3 (Services): 30 minutes
  • Phase 4 (Tests): 45 minutes
  • Phase 5 (Integration): 20 minutes

Total: ~2 hours


Notes

  • Don't skip phases — order matters
  • Each phase builds on the previous one
  • If a phase doesn't make sense, stop and re-read the plan
  • Commit after each phase, even if it's not "done"
  • Code review is easier when you follow the plan exactly