4.8 KiB
Flow Pattern Library
Common architecture patterns and how to identify them in code.
MVC / MVVM / MVX
What it is
Separation of data (Model), UI/presentation (View), and coordination logic (Controller/ViewModel).
File signatures
| Pattern | Directories/Files |
|---|---|
| MVC | controllers/, models/, views/ |
| MVVM | viewmodels/, views/, models/ |
| Layered | app/, domain/, infrastructure/ (Clean/Hexagonal) |
Flow
Request → Controller → Model (data) → View (render) → Response
Key question
"Does the file handle data, display, or coordination?" If yes → MVC-family.
Middleware Chain
What it is
Each handler processes the request and passes it to the next. Like an assembly line.
File signatures
| Framework | Indicator | |
|---|---|---|
| Express/Koa | app.use(...), app.get('/', handler) |
|
| FastAPI | @app.middleware("http"), Depends() |
|
| Next.js | middleware.ts at root or in app/ |
|
| Gin (Go) | router.Use(middleware1, middleware2) |
|
| Koa | app.use(async (ctx, next) => { ... }) |
Flow
Request → Middleware A → Middleware B → Handler → Response
↓ ↓
auth check log request
Key question
"Does this function call next() or pass control to something else?" If yes → middleware.
Common middleware order
1. CORS / Security headers
2. Logging / Request ID
3. Authentication / Authorization
4. Body parsing / Validation
5. Rate limiting
6. Route handler
7. Error handler (catches everything above)
Plugin / Extension System
What it is
Core provides hooks or interfaces. External code registers handlers. The core doesn't know about specific plugins.
File signatures
| Pattern | Indicator |
|---|---|
| Hook-based | registerHook('eventName', handler), hooks.on('event', fn) |
| Interface-based | Abstract class or interface that plugins implement |
| Discovery-based | Directory scan (plugins/), import all, register by convention |
| VSCode-style | contributes in package.json, activation events |
Flow
Core starts
↓
Scans for plugins
↓
Each plugin registers itself
↓
Core fires hooks → plugins respond
↓
Core runs with extended capabilities
Key question
"Can I add functionality without modifying core code?" If yes → plugin architecture.
Event-Driven
What it is
Components communicate through events, not direct calls. Publishers emit, subscribers listen.
File signatures
| Pattern | Indicator |
|---|---|
| Node EventEmitter | eventEmitter.on('event', handler), eventEmitter.emit('event', data) |
| Pub/Sub | pubsub.subscribe('channel', handler), pubsub.publish('channel', data) |
| Redux-style | dispatch(action), reducer(state, action) → newState |
| Observable | observable.subscribe(fn), pipe(map, filter) |
| Signals (Python) | @signal.connect, signal.send() |
Flow
Component A emits "user.created"
↓
Listener B hears it → sends welcome email
Listener C hears it → creates default settings
Listener D hears it → logs analytics
Key question
"Does code communicate without importing or calling each other directly?" If yes → event-driven.
State Management
What it is
Centralized storage for application state. Components read and update through defined interfaces.
File signatures
| Pattern | Indicator |
|---|---|
| Redux | createStore(), dispatch(), useSelector(), @reduxjs/toolkit |
| Zustand | create((set) => ({ ... })) |
| Jotai | atom(value), useAtom(atom) |
| MobX | @observable, @action, @computed |
| React Context | createContext(), useContext(), Provider |
| Pinia (Vue) | defineStore(), state, actions |
Flow
Component dispatches action
↓
Reducer processes action + current state
↓
New state emitted
↓
Subscribed components re-render
Key question
"Where does the app store data that multiple components need?" If it's a single store → state management pattern.
Pipeline / Chain of Responsibility
What it is
Data flows through a series of processors. Each processor transforms the data and passes it on.
File signatures
| Pattern | Indicator |
|---|---|
| Stream processing | .pipe(transform1).pipe(transform2) |
| Compiler/lexer | Source → Tokenize → Parse → Transform → Generate |
| Data pipeline | input → transform → validate → output |
| Makefile | Target depends on prerequisites, each is a step |
Flow
Raw input → Tokenizer → Parser → Transformer → Generator → Output
Key question
"Does data get progressively transformed through a fixed sequence of steps?" If yes → pipeline.