Skip to content

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-users and /api/v1/user-invites listed as separate table rows (lines 221-222) but line 222 says "Alias route" — not a separate module
  • MenuBuilder directory covers both /menus and /menu-items as 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.tsBlog, User.tsUser).

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 plus cloud/s3.ts and email/resend.ts in 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.json exactly
  • 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.json scripts
  • 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.ts typo was fixed (renamed to auth.controller.ts)
  • Both config typos (nod_env, sesseion_secret) confirmed in config/index.ts
  • All Stripe webhook events (6) and LemonSqueezy webhook events (5) match code
  • rateLimiter is auth-routes-only (confirmed)
  • sanitizeQuery is blog-list-only (confirmed)
  • hasPermission.ts is dead code (confirmed, zero imports)
  • permissionMiddleware.ts exports permission() and hasAnyPermission() (confirmed)
  • permissionCache.ts uses Set<string> with ownership validation (confirmed)
  • permissionNormalizer.ts does bidirectional format conversion (confirmed)

Generated by adversarial review — 8 parallel verification agents, 2026-03-12