Part 4: Hybrid and Advanced Techniques¶
15. Hybrid Folder Structures¶
“You don’t have to choose between modular and scalable.
You can evolve through them.”
What is a Hybrid Folder Structure?¶
A hybrid architecture blends feature-first modularity with layered scalability.
You keep the independence of modules (e.g., chatbot/, invoice/)
But you extract global logic (e.g., GPT, OCR, auth, logging) into reusable layers like services/, core/, and shared/.
It’s not either-or.
It’s both—with boundaries.
Hybrid structure is often a transition state, but it’s also a long-term strategy for many growing apps.
When Should You Go Hybrid?¶
You’ve outgrown pure modularity but aren’t ready to go full scalable.
Use hybrid when:
- You want to preserve feature ownership
- You need to centralize GPT, OCR, or vector DB logic
- Your React app has reusable UI systems + scoped features
- Your FastAPI backend has shared schemas, background tasks, or infra
In short: you're building an AI-first product that must scale over time—without losing its feature velocity.
React: Hybrid Folder Example¶
src/
├── features/
│ ├── chatbot/
│ │ ├── ChatPage.tsx
│ │ ├── hooks/
│ │ ├── api/
│ │ └── __tests__/
│ └── invoice/
│ ├── UploadPage.tsx
│ ├── components/
│ └── __tests__/
├── api/ # Global API clients
├── services/ # Shared GPT, auth, upload logic
├── components/ # Shared UI components
├── hooks/ # Global utilities
├── context/ # Auth, theme, toast providers
└── store/ # Global state
You preserve feature folders for UI flow, but global workflows are abstracted to /services/.
Example:
useChatSession()→ usesgptService.generateResponse()uploadInvoice()→ usesuploadService.sendFile()
FastAPI: Hybrid Folder Example¶
app/
├── chatbot/
│ ├── api/
│ ├── schemas/
│ ├── __tests__/
├── invoice/
│ ├── api/
│ └── __tests__/
├── api/ # Versioned routing entry points
├── services/ # GPT, embeddings, invoice parsing
├── schemas/ # Shared schemas (e.g., pagination, base models)
├── infra/ # DB, vector store, Celery, Redis
├── tasks/ # Background jobs
├── core/ # Config, security, middlewares
└── main.py
Each feature still has local routes, but GPT logic is unified under services/gpt_service.py.
This allows:
- Parallel feature work
- Centralized optimization of shared layers
- Modular tests + scalable deployment
Evolving from Modular → Hybrid → Scalable¶
Here’s a safe evolution path:
| Stage | Folder Traits |
|---|---|
| 1. Modular | Feature-first: chatbot/, invoice/, upload/ |
| 2. Hybrid | Extract shared logic into /services/, /shared/, /infra/ |
| 3. Scalable | Add strict layering: /api, /core, versioned APIs, DI, repository pattern, CI testing scopes |
Use refactor points as checkpoints:
- GPT logic duplicated? Extract to
services/ - OCR utils copy-pasted? Move to
shared/ocr.py - Tests cross module boundaries? Split into
integration/vsunit/ - CI/CD too slow? Use per-layer workflows
Hybrid Folder Design Goals¶
| Goal | Design Strategy |
|---|---|
| Preserve modular feature flow | Keep features/ or scoped chatbot/ folders |
| Enable shared logic reuse | Extract to services/, shared/, infra/ |
| Avoid tight coupling | Use dependency injection or shared interfaces |
| Keep tests isolated | Per-feature __tests__/, global mocks in test_helpers/ |
| Support CI pipelines | Build/test/deploy by folder scope (e.g., backend only if changed) |
Migration Patterns: How to Transition Gracefully¶
Pattern: “Move First, Test Later”¶
- Duplicate shared logic into
services/ - Use feature logic first
- Then write unit tests for
services/
Pattern: “Wrapper Injection”¶
- Refactor GPT calls in
chatbot/into aGPTServiceclass - Inject into multiple modules via shared
services/
Pattern: “Refactor by Volume”¶
- Identify most-used utilities
- Extract only those
- Avoid premature generalization
Pattern: “Use Mocks to Abstract Away”¶
- Test
chatbot/using fakeGPTServicebefore moving the real one
These patterns let you gradually scale without breaking working modules.
Summary: Hybrid Folder Structures¶
| Principle | Application |
|---|---|
| Modularity gives ownership | Keep features/ or modules/ for UI/business domains |
| Scalability gives reuse | Extract to /services/, /shared/, /infra/ |
| Don’t generalize too early | Refactor real patterns only |
| Use folder structure as a signal | Is logic reused? Move it. Is it tightly scoped? Keep it local |
| Test per module, share mocks globally | test_helpers/ supports the hybrid layer boundary |
Hybrid isn’t a compromise.
It’s a mature middle state—ready to support real AI workflows, rapid iteration, and future scale.