Adversarial Review — docs/architecture/backend.md¶
Reviewed: 2026-03-12 | Method: 8 parallel agents — exhaustive code verification + internal consistency analysis Scope: Every claim in backend.md cross-checked against actual codebase (
saas-boilerplate/src/) Total findings: 45 (7 Critical, 8 High, 14 Medium, 16 Low)
Critical (7)¶
C1. 5 entity table names fabricated with underscores¶
The doc claims snake_case table names for 5 entities, but these use bare @Entity() with no explicit table name argument. TypeORM defaults to the lowercase class name with no underscores.
| Entity | Doc claims | Actual (TypeORM default) |
|---|---|---|
| OtpVerification | otp_verification |
otpverification |
| LoginAttempt | login_attempt |
loginattempt |
| PackageCategory | package_category |
packagecategory |
| BlogCategory | blog_category |
blogcategory |
| ProductCategory | product_category |
productcategory |
Impact: Anyone relying on this doc for raw SQL queries, migrations, or external tooling will target wrong table names.
C2. "~233 Endpoints" vastly undercounted¶
Sampling Auth (16 routes), User (13), Blog (11) gives an average of ~12 per module x 30 = ~360. The actual endpoint count is likely 300-400+, not 233.
Location: Line 191
C3. "refresh token rotation" is wrong terminology¶
AuthService.refreshToken() (auth.service.ts:455-470) only generates a new access token from an existing refresh token. No new refresh token is ever issued. This is "access token refresh," not "refresh token rotation" (which implies the refresh token itself is replaced on each use).
Locations: Line 181 ("JWT authentication with refresh token rotation")
C4. LemonSqueezy payment factory NOT implemented¶
PaymentFactory.ts declares LemonSqueezy in its type signature but has no case statement for it. Calling the factory with LemonSqueezy will throw "Unsupported payment provider" at runtime.
Location: Line 382 claims LemonSqueezy as a supported payment provider
File: src/lib/payment/PaymentFactory.ts
C5. Local storage provider NOT integrated¶
LocalSotrage.ts exists as a file but StorageFactory.ts only handles cloudinary and aws in its switch statement. The "Local" option is dead code.
Location: Line 382 claims 3 storage providers (Cloudinary/S3/Local); only 2 work
File: src/lib/storage/StorageFactory.ts
C6. eventEmmiter.ts typo claim is fabricated¶
The doc states: eventEmmiter.ts [sic — typo in filename] (lines 94, 341). The actual file is correctly named eventEmitter.ts. The documented typo does not exist in the codebase.
File: src/app/events/eventEmitter.ts (correctly spelled)
C7. "Moved from shared/ to lib/" is false¶
The doc says service factories were "moved from src/shared/ to src/lib/ in a recent refactor" (line 375). Legacy cache.ts still exists in src/shared/. The factories were duplicated, not moved.
Files: src/shared/cache.ts (legacy, still present) + src/lib/cache/Node-cache.ts (new)
High (8)¶
H1. 3 undocumented directories under src/app/¶
The directory tree (lines 56-151) omits three directories that exist and contain active code:
| Directory | File | Purpose |
|---|---|---|
src/app/factories/ |
sanitizer/sanitizer.factory.ts |
Sanitizer factory (DI pattern) |
src/app/infrastructure/ |
sanitizer/dompurify.sanitizer.ts |
DOMPurify sanitizer implementation |
src/app/transporter/ |
PostgresTransport.ts |
Winston PostgreSQL transport |
H2. Entity count: claims 29, lists only 28¶
The Database Schema tables (lines 244-294) enumerate 28 entities. The 29th is UserRole (file: UserRoles.ts), which exists as an explicit entity class with @Entity(), @ManyToOne relations, and @DeleteDateColumn — but is never mentioned in the doc. The doc treats user_roles only as an implicit junction table.
H3. Module count ambiguity¶
/api/v1/invite-usersand/api/v1/user-inviteslisted as separate table rows (lines 221-222) but line 222 says "Alias route" — not a separate module- MenuBuilder directory covers both
/menusand/menu-itemsas one folder with two sub-modules - The "30" count is technically correct but the presentation is misleading
Location: Line 191
H4. "227 lines of dead code removed" is unverifiable¶
Line 76 claims auth.ts had "227 lines of dead code removed." Current auth.ts is 135 lines total. If 227 lines were removed, the original file would have been 362+ lines. No evidence of this exists. This also contradicts the "cleanup completed Mar 8-11" claim on line 419.
H5. 3 dead event types not flagged as dead code¶
OTP_VERIFIED, RESET_PASSWORD, VERIFY_EMAIL_REG are defined in eventTypes.ts but have zero handlers registered in events/index.ts. The doc says "Additional event types defined but with less clear handler mapping" (line 353) — they have no mapping. These are dead code constants.
File: src/app/events/eventTypes.ts
H6. .route.ts vs .routes.ts naming inconsistency undocumented¶
The Architecture Pattern section (lines 43-51) shows the template as {domain}.routes.ts (plural). In reality:
.routes.ts(plural) — 19 modules: Analytic, AppConfig, Blog, BlogCategory, BlogTag, Cupon, EmailTemplate, GenericPage, Package, PackageCategory, Permission, Product, ProductCategory, Public, Role, Setting, Slug, Subscription, User, UserSetting.route.ts(singular) — 8 modules: Auth, Contact, Image, InviteUser, Payment, Purchase, Swagger, Webhook
No consistent convention. The doc acknowledges none of this.
H7. seed/data/ subdirectory undocumented¶
src/seed/ contains a data/ subdirectory with permission.ts and setting.ts seed data files. Previous agent found only 4 top-level files; actual total is 6 files across 2 levels. The doc (line 144) says only "Database seeders (admin, roles, permissions)" without structure detail.
H8. Env property count self-contradicts¶
- Lines 62, 149: "29 variable groups"
- Line 421: "30+ Properties"
These cannot both be correct.
Medium (14)¶
M1. MVC pattern compliance overstated¶
The doc claims a uniform "Modular MVC with Service Layer" pattern (lines 43-51). Actual compliance:
| Status | Count | Modules |
|---|---|---|
| Fully compliant (4/4 files) | 18 | Analytic, AppConfig, Blog, BlogCategory, BlogTag, Cupon, EmailTemplate, GenericPage, Package, PackageCategory, Permission, Product, ProductCategory, Role, Setting, Slug, Subscription, User, UserSetting |
| Partial (missing files or naming) | 10 | Auth, Contact, Image, InviteUser, Payment, Public, Purchase, Webhook, MenuBuilder, package |
| Non-compliant | 2 | Logger (service-only), Swagger (route-only) |
M2. 3 modules missing validation files; 1 has empty validation¶
| Module | Issue |
|---|---|
| Image | No image.validation.ts |
| Public | No public.validation.ts |
| Purchase | No purchase.validation.ts |
| Setting | setting.validation.ts exists but is 0 bytes (empty file) |
M3. contextMiddleware listed as both global and per-route¶
Line 167 lists it as global middleware step #10. Line 182 lists it again as per-route middleware. If registered globally, why list it per-route? The dual-path behavior (OAuth gets context, JWT short-circuits) is not explained in this doc.
M4. solution docs/ location wrong in directory tree¶
The directory tree (line 143) shows solution docs/ under src/. It actually exists at saas-boilerplate/solution docs/ — a sibling to src/, not inside it.
M5. subscribtionExpire.ts not labeled as typo¶
sequrity.ts gets [sic] on line 86. The fabricated eventEmmiter.ts gets [sic — typo in filename] on line 341. But subscribtionExpire.ts (line 101) and resetSubscribtionStatus.handler.ts get no typo annotation. Also missing: LocalSotrage.ts typo never mentioned anywhere.
M6. Image/Public/Slug modules have no entity mapping¶
These modules appear in the API table (lines 215-217) but the doc never explains what data they operate on. No corresponding entities exist in the Database Schema section.
M7. hasPermission.ts understated as "mostly dead"¶
Line 77 says "mostly dead code." Line 323 says "retained but rarely applied." Grep confirms zero imports anywhere in the codebase — it is 100% dead code, not "mostly" or "rarely."
M8. "Common CRUD Pattern" claim unjustified¶
Lines 231-240 list 9 operations and claim "Most modules implement" them. Auth, Public, Slug, Analytic, Swagger, and Logger clearly don't follow this pattern. "Most" is at best debatable without quantification.
M9. ContactMessage entity vs Contact module naming gap¶
Entity class: ContactMessage. Table: contact_messages. Module folder: Contact. API path: /contacts. The naming mismatch between entity and module is never acknowledged.
M10. Session-to-JWT handoff for OAuth unexplained¶
Line 314: "Google OAuth → Session handoff → JWT issuance." The doc never describes how the Express session bridges to JWT token generation after OAuth callback.
M11. Loose stripe.service.ts at module root is an architectural violation¶
src/app/modules/v1/stripe.service.ts exists directly in the modules root, outside any module folder. Multiple agents flagged this as a significant violation of the claimed "Modular MVC" architecture. The doc acknowledges it in a note (line 227) but treats it as trivia rather than a structural problem.
M12. Token entity purpose and lifecycle not clearly described¶
The Token entity table (line 256) lists id, userId, token (SHA-256 hash), expiresAt but never explains what tokens it stores (password reset? email verification? both?), how they're created, or when they're cleaned up.
M13. MenuBuilder hierarchical structure undocumented¶
The MenuBuilder module contains Menu/ and MenuItem/ subdirectories, each with a complete MVC file set (controller + service + routes + validation). This deviates from the claimed flat module structure and is not mentioned in the Architecture Pattern section.
M14. package/ directory uses lowercase¶
All other module directories use PascalCase (Auth, Blog, User). The package directory is lowercase. On case-insensitive Windows, this could shadow or collide with a Package/ directory.
Low (16)¶
L1. Role module has undocumented role.repository.ts¶
Extra data access layer file beyond the 4-file MVC pattern. Not mentioned in the Architecture Pattern section or module notes.
L2. Subscription module has undocumented subscription.helpers.ts¶
Extra helpers file beyond the pattern template (which only shows {domain}.utils.ts as the optional 5th file, not .helpers.ts).
L3. Status enum capitalization inconsistent across entities¶
- InviteUser:
PENDING,ACCEPTED(UPPER_CASE) - Subscription:
active,expired,pending,cancelled(lowercase) - Payment:
pending,completed,failed,refunded(lowercase) - Blog:
draft,published,archived(lowercase)
The doc reflects this accurately but doesn't flag the inconsistency.
L4. Significant dependencies missing from Tech Stack table¶
These are in package.json but absent from the Technology Stack table (lines 12-39):
bcrypt, multer, pg, dotenv, uuid, axios, reflect-metadata, http-status, passport-google-oauth20, winston-daily-rotate-file, express-winston, yaml, yamljs, copyfiles
L5. No engines field in package.json¶
Doc claims "Node.js 18.17+" (line 14) but package.json has no engines field to enforce this.
L6. "Generated" date should be "Last Modified"¶
Header says "Generated: 2026-02-12" but content was modified on 2026-03-12 (security improvements added on line 431). The date label is misleading.
L7. lib/ subdirectory file counts not documented¶
lib/email/ has 10 files including a template/ subdirectory. lib/payment/ has 4 files. lib/storage/ has 5 files. The doc mentions these directories but doesn't detail their contents, unlike shared/ and helper/ which get full file listings.
L8. Blog.ts has unused categoryId field alongside many-to-many relationship¶
Blog.ts declares a categoryId column (line 83) and has an index referencing it (line 40), but the actual Blog-to-Category relationship uses a many-to-many junction table (blog_categories_pivot). The categoryId field is dead code.
L9. Entity filename Otp.ts vs class name OtpVerification mismatch¶
The entity file is named Otp.ts but the class inside is OtpVerification. This breaks the naming convention where filename matches class name (e.g., Blog.ts → Blog, User.ts → User).
L10. Cupon.ts entity filename typo not called out in doc¶
The doc notes the module directory name Cupon and API path /coupons but never explicitly flags that the entity file itself is misspelled (Cupon.ts should be Coupon.ts).
L11. Route registration order (steps 11-16) slightly inaccurate¶
The doc shows root / welcome route as step 11, but in app.ts the webhook routes are registered first (line 23), root route second (line 25), then static files (line 31). The actual order differs from the numbered sequence in the doc.
L12. File naming convention inconsistencies across modules¶
Beyond .route.ts vs .routes.ts (H6), there are additional naming pattern splits:
- Hyphenated: generic-page.controller.ts, invite-user.controller.ts, package-category.controller.ts
- Underscored: app_config.controller.ts, blog_category.controller.ts, user_setting.controller.ts
- camelCase: menuItem.controller.ts
- Plain: blog.controller.ts, user.controller.ts
No consistent convention across modules.
L13. interfaces/ directory has 8 files including sanitizer/ subdirectory — not detailed in doc¶
Doc says "TypeScript interfaces" (line 103) but doesn't list the 8 files: error.ts, file.ts, pagination.ts, subscription.ts, config.interface.ts, express-session.d.ts, index.d.ts, plus sanitizer/sanitizer.interface.ts.
L14. builder/ directory has 2 files — not detailed in doc¶
Doc says "Query builders" (line 116) but doesn't mention the specific files: getQueryBuilder.ts and QueryBuilder.ts.
L15. errors/ directory has 2 files — doc implies only 1¶
Doc says "Custom ApiError class" (line 102) implying a single file. Actually contains ApiError.ts AND handleZodError.ts.
L16. Which routes use legacy vs active permission system is unclear¶
M7 establishes hasPermission.ts is 100% dead code, but the doc's Permission System section (lines 318-327) describes "Active system" and "Legacy system" without clarifying that the legacy system is used by exactly zero routes. A reader might assume some routes still use it.
Corrections to Initial Review¶
One finding from the initial review round was corrected by the deep scan:
- Helper count IS 24 (not 22 as initially reported) —
src/helper/contains 22 top-level files pluscloud/s3.tsandemail/resend.tsin subdirectories, totaling 24. The doc's claim is correct.
Verified Accurate Claims¶
The following doc claims were verified as correct across all 8 agents:
- All 27 technology stack versions match
package.jsonexactly - 14 middleware files count is correct
- 30 module directories exist (count matches)
- 29 entity files exist (count matches)
- 15 shared utility files count is correct
- 7 event handler files count is correct
- 10 policy files count is correct
- 8 config files count is correct
- All dev commands match
package.jsonscripts - TypeScript config (ES2016, CommonJS, strict) is correct
- Port 5500 is specified in
.env.example - "All entities use
@DeleteDateColumn" — confirmed for all 29 - Middleware pipeline execution order 1-10 matches
registerMiddlewares()exactly - Helmet IS applied (contradicts MEMORY.md "Helmet disabled" claim)
auth.contoroller.tstypo was fixed (renamed toauth.controller.ts)- Both config typos (
nod_env,sesseion_secret) confirmed inconfig/index.ts - All Stripe webhook events (6) and LemonSqueezy webhook events (5) match code
rateLimiteris auth-routes-only (confirmed)sanitizeQueryis blog-list-only (confirmed)hasPermission.tsis dead code (confirmed, zero imports)permissionMiddleware.tsexportspermission()andhasAnyPermission()(confirmed)permissionCache.tsusesSet<string>with ownership validation (confirmed)permissionNormalizer.tsdoes bidirectional format conversion (confirmed)
Generated by adversarial review — 8 parallel verification agents, 2026-03-12