This document outlines the non-negotiable rules for developing on the LogicSpike platform. Violating these rules corrupts the architecture.
🏗️ 1. Architecture Rules
🚫 Rule 1.1: The Gateway Mandate
- Rule: NEVER expose a Microservice directly to the public internet.
- Why: The Gateway is our shield. It handles Rate Limiting, Authentication, and SSL.
- Flow: Client ->
Gateway->Private Service(Manager/Blog/etc). - Enforcement: Services should bind to private ports/IPs only, or verify a shared secret from the Gateway.
🛡️ Rule 1.3: The Gateway Key (Production Grade)
- Rule: All internal services MUST verify the
x-gateway-keyheader. - Implementation:
- NEVER Hardcode: Use
process.env.GATEWAY_SECRET. - Rotation Support: Services should accept a comma-separated list of secrets (Current + Previous) to allow rotation without downtime.
- Length: Minimum 32 characters, high entropy.
- NEVER Hardcode: Use
- Behavior: If the key is missing or invalid, return
403 Forbidden.
🔄 Rule 1.4: Service Isolation
- Rule: Service A CANNOT import code or database models from Service B.
- Why: Coupling kills microservices. If the Blog service changes its DB schema, it shouldn't break the Billing service.
- Communication: Use HTTP APIs (REST) or Events (Queue) to talk between services.
💾 2. Database Rules
🏢 Rule 2.1: The Tenant Barrier
- Rule: Every query touching "Floor Level" data MUST include
WHERE tenant_id = ?. - Why: Omitting this is a P0 Security Vulnerability (Data Leak).
- Exception: "Building Level" tables (
users,tenants) do not have tenant IDs.
⛓️ Rule 2.2: No Cross-Service Joins
- Rule: You cannot write a SQL JOIN between the
userstable (Manager) and thepoststable (Blog Service). - Why: They might live in different databases physically (or in the future).
- Solution: Fetch the User ID, then fetch the Posts. Stitch them in code.
🔐 3. Security Rules
🔑 Rule 3.1: Secrets Management
- Rule: NEVER commit
.envfiles or API keys to Git. - Why: If a key leaks, we are compromised.
- Enforcement: Use
.gitignoreand environment variables.
🛡️ Rule 3.2: Authentication Context
- Rule: NEVER trust the User ID sent in the request body.
- Why: A malicious user can send
{ "user_id": "admin_id" }. - Solution: ALWAYS derive identity from the validated JWT token in the
Authorizationheader.
💻 4. Coding Discipline
🧩 Rule 4.1: Strong Typing
- Rule: Avoid
anyin TypeScript. - Why: It defeats the purpose of using TypeScript. Define interfaces for everything.
📢 Rule 4.2: Standard Response Envelope
- Rule: All APIs must return a standard structure.
- Format:
{
"data": { ... }, // The payload
"error": null, // Or error object
"meta": { ... } // Pagination, timestamps
}🚨 Rule 4.3: Error Handling Standard
- Rule: Throw structured errors, don't just return 500.
- Codes:
400 Bad Request: Validation failure (zod).401 Unauthorized: No token / Invalid token.403 Forbidden: Valid token, but wrong permissions (rolecheck).404 Not Found: ID doesn't exist or belongs to another tenant.429 Too Many Requests: Rate limit hit.
- Format:
{
"data": null,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The member 'ben' was not found in this workspace.",
"doc_url": "..."
}
}🧪 5. Testing & Quality
� Rule 5.1: The "Critical Path" Test
- Rule: Every core feature (Login, Invite, Checkout) MUST have an Integration Test.
- Why: Unit tests pass, but systems fail. We need to know if the flow works.
🧹 Rule 5.2: Clean Commits
- Rule: Follow Conventional Commits (
feat:,fix:,chore:). - Why: Auto-generates changelogs and makes history readable.
🎨 6. Frontend Standards (Next.js)
⚛️ Rule 6.1: Server vs Client
- Rule: Default to Server Components. Use Client Components (
"use client") only for interactivity (onClick, useState). - Why: Performance and SEO.
🚫 Rule 6.2: No Direct DB Access
- Rule: UI Components NEVER import
prismaordrizzledirectly. - Solution: Call a Server Action or an API Route.
🐌 Rule 6.3: Lazy Loading Strategy
- Rule: Heavy UI components (Editors, Charts, Maps) MUST be lazy loaded using
next/dynamic. - Why: Reduces the initial JavaScript bundle size, making the page load faster (LCP/TBT).
- Pattern:
import dynamic from 'next/dynamic'
// ❌ Bad
import { HeavyChart } from './HeavyChart'
// ✅ Good
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <p>Loading Chart...</p>,
ssr: false // Only if it doesn't need SEO
ssr: false // Only if it doesn't need SEO
})📡 Rule 6.4: API Client Layer
- Rule: Components NEVER call
fetch()directly. - Why: Centralizes auth headers, error handling, and type definitions.
- Pattern:
- Create
src/modules/{feature}/api.ts. - Export typed async functions
fetchPosts(),createPost(). - Call these functions from UI components or Actions.
- Create
🎣 Rule 6.5: Data Fetching Hooks
- Rule: Use a Hook for data fetching, don't just
useEffect->fetch. - Why: Handles
isLoading,isError, anddatastates consistently. - Recommendation: Adopt TanStack Query (React Query) or SWR for caching and deduplication as we scale. Code is currently using primitive
useEffector direct calls, which causes waterfalls.
📦 7. Monorepo Architecture (Turbo)
🧱 Rule 7.1: The "App as Consumer" Pattern
- Rule: Apps (
apps/*) should contain minimal logic. - Why: Business logic trapped in a Next.js app is hard to reuse (e.g., in a Worker or CLI).
- Pattern:
packages/core-business: Domain logic (User creation, Billing calc).packages/db: Database schema and connection.apps/manager: Importscore-businessto verify a user.apps/worker: Importscore-businessto process a background job.
🚧 Rule 7.2: No Cross-App Imports
- Rule:
apps/seller-dashboardMUST NOT import fromapps/manager. - Why: Apps are capable of being deployed independently. Importing creates a hidden monolith.
- Solution: Move shared code to a
packages/*library.
📌 Rule 7.3: Explicit Dependencies
- Rule: All internal packages must be listed in
package.jsonwithworkspace:*. - Why: Turbo relies on this graph to build correctly.
📜 Summary
- Gateway First.
- Gateway First.
- Tenant ID Always.
- No Cross-Imports.
- Trust the Token, not the Body.
- Standard Errors & Types.