Folder Structure — Architecture Rationale¶
Project Structure tells you where things are. This page explains why, the layering (such as it is), and the conventions to follow so new code fits in.
1. The de-facto layering¶
Prism does not use a classic Controller → Service → Repository architecture. Its real layering is:
flowchart TD
R[routes/*.js<br/>URL → controller.fn + middleware] --> C[controllers/*.js<br/>request handling + a LOT of business logic]
C --> CF[commonFunctions/*.js<br/>reusable helpers, queue producers, senders]
C --> PL[processLibrary/*.js<br/>batch/aggregation engines]
CRON[crons/*.js<br/>schedulers] --> CF
CRON --> PL
CF --> M[(MongoDB via mongoose/native)]
CF --> S[(MySQL via raw SQL)]
PL --> M
PL --> S
C --> M
C --> S
| Layer | Responsibility | Reality check |
|---|---|---|
routes/ |
Map URL+method → controller fn, attach auth | Clean and thin ✅ |
controllers/ |
Handle req/res | ⚠️ Also contain heavy business logic (some files 50+ functions) |
commonFunctions/ |
Shared, reusable operations | The closest thing to a "service" layer |
processLibrary/ |
Long-running batch/aggregation | "The batch brains" |
crons/ |
Time-based entry points | Thin wrappers that call the above |
models/ |
Mongoose schemas only | MySQL has no models — raw SQL inline |
The key architectural fact: there is no dedicated service/repository layer, and MySQL access is raw SQL scattered across controllers, commonFunctions, and crons. This is the dominant source of duplication and the hardest thing for newcomers. Treat
commonFunctions/as the place to extract shared logic into.
2. Naming conventions observed¶
| Pattern | Meaning | Examples |
|---|---|---|
*Controller.js |
route handlers for a feature | campaignController.js, loyaltyController.js |
*OrderIngestion.js |
per-source ingestion | petpoojaOrderIngestion.js, ristaOrderIngestion.js |
*Queue.js (commonFunctions) |
queue producer/consumer helper | smsCampaignQueue.js, pushCampaignQueue.js |
send*.js (commonFunctions) |
channel senders | sendSms.js, sendWhatsapp*, sendFcmNotification.js |
*Cron.js / crons/* |
scheduled jobs | abandonCartCron.js, rfmSegments.js |
*MIS* / *mis* |
reporting/aggregation | campaignMis.js, dashboardMIS.js, models/*_mis.js |
handle*Flow.js |
multi-step domain flows | handleWalletPointsFlow.js, handleMilestoneUpgrade.js |
process*.js |
batch processors | processCommonIngestion.js, processAutomatedCampaigns.js |
*New / v2 / Dynamic suffix |
⚠️ newer parallel implementation coexisting with the old | dashboardControllerNew.js, crmv2.js, campaignControllerDynamic.js, automatedCampaignQueueNew.js |
The
*New/Dynamic/v2suffixes are a migration tell: two implementations of the same thing run side by side. Always confirm which one is wired inroutes//index.jsbefore editing. See Module Architecture.
3. Subfolders inside crons/¶
| Subfolder | Purpose |
|---|---|
autoCampaignRoiCrons/ |
ROI attribution for automated campaigns |
rewardsCrons/ |
loyalty/reward periodic jobs |
updationCrons/ |
data-update/maintenance jobs |
4. Where to put new code (guidelines)¶
| You're adding… | Put it in… | And… |
|---|---|---|
| A new endpoint | controllers/<feature>.js + register in routes/ |
keep the controller thin; push logic to commonFunctions/ |
| Reusable logic | commonFunctions/ |
so multiple controllers/crons share it |
| A scheduled job | crons/<name>Cron.js + require in index.js |
guard against overlap (status doc); use jobsPool for MySQL |
| A batch/aggregation | processLibrary/ |
write results to a MIS collection |
| A new Mongo collection | models/<name>.js |
mongoose schema + indexes |
| MySQL access | inline (no ORM) — but centralize the query in commonFunctions/ if reused |
always parameterize (avoid SQL injection) |
5. Anti-patterns to avoid (learned from the codebase)¶
- ❌ Copy-pasting a controller and suffixing
New/v2— extend or refactor instead. - ❌ Writing raw SQL inline that already exists elsewhere — search
commonFunctions/first. - ❌ Adding a cron without overlap protection or without
jobsPool. - ❌ Cross-store writes (Mongo + MySQL) without idempotency — see the loyalty points cautionary tale in Loyalty.
- ❌ Hardcoding secrets/hosts — use
config.js/ env.