From 6ea61d1dfee1a37ab27cb44e4b384b06ecd58e21 Mon Sep 17 00:00:00 2001 From: rd-code-23 Date: Tue, 18 Aug 2026 20:35:30 -0700 Subject: [PATCH] refactor(auth): clarify module responsibilities --- AGENTS.md | 6 ++++++ .../google-oidc.routes.test.ts} | 4 ++-- .../{google-oidc.ts => google/google-oidc.routes.ts} | 6 +++--- .../google-user.service.test.ts} | 2 +- .../{google-user.ts => google/google-user.service.ts} | 2 +- .../session.middleware.test.ts} | 2 +- .../{session.ts => session/session.middleware.ts} | 2 +- apps/api/src/server.ts | 6 +++--- docs/architecture.md | 11 +++++++++++ 9 files changed, 29 insertions(+), 12 deletions(-) rename apps/api/src/auth/{google-oidc.test.ts => google/google-oidc.routes.test.ts} (98%) rename apps/api/src/auth/{google-oidc.ts => google/google-oidc.routes.ts} (96%) rename apps/api/src/auth/{google-user.test.ts => google/google-user.service.test.ts} (96%) rename apps/api/src/auth/{google-user.ts => google/google-user.service.ts} (95%) rename apps/api/src/auth/{session.test.ts => session/session.middleware.test.ts} (98%) rename apps/api/src/auth/{session.ts => session/session.middleware.ts} (96%) diff --git a/AGENTS.md b/AGENTS.md index f051fb1..2cb3682 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -131,6 +131,12 @@ locations. Do not create shared abstractions before there is a real reuse case. +Within a feature, use descriptive role suffixes when they make a file's +responsibility clearer, such as `.routes.ts`, `.service.ts`, `.middleware.ts`, +and `.schema.ts`. Colocate tests as `..test.ts`. Group related +capabilities into focused subfolders when a feature grows; avoid broad files +such as `auth.service.ts` or empty layers created only to match a template. + ## Frontend Standards diff --git a/apps/api/src/auth/google-oidc.test.ts b/apps/api/src/auth/google/google-oidc.routes.test.ts similarity index 98% rename from apps/api/src/auth/google-oidc.test.ts rename to apps/api/src/auth/google/google-oidc.routes.test.ts index 313da48..d976f4a 100644 --- a/apps/api/src/auth/google-oidc.test.ts +++ b/apps/api/src/auth/google/google-oidc.routes.test.ts @@ -10,8 +10,8 @@ import { type ExchangeAuthorizationCode, type OidcFlowState, validateReturnTo, -} from './google-oidc.js'; -import { createSessionMiddleware } from './session.js'; +} from './google-oidc.routes.js'; +import { createSessionMiddleware } from '../session/session.middleware.js'; const redirectUri = 'http://localhost:3000/auth/google/callback'; const sessionConfiguration = { diff --git a/apps/api/src/auth/google-oidc.ts b/apps/api/src/auth/google/google-oidc.routes.ts similarity index 96% rename from apps/api/src/auth/google-oidc.ts rename to apps/api/src/auth/google/google-oidc.routes.ts index c622a0b..c4011f1 100644 --- a/apps/api/src/auth/google-oidc.ts +++ b/apps/api/src/auth/google/google-oidc.routes.ts @@ -1,4 +1,4 @@ -/** Starts Google OIDC authorization with protected state, nonce, and PKCE data. */ +/** Defines secure Google OIDC login-start and callback routes. */ import { Router } from 'express'; import { buildAuthorizationUrl, @@ -12,8 +12,8 @@ import { } from 'openid-client'; import { z } from 'zod'; -import type { Env } from '../config/env-schema.js'; -import type { GoogleUserProfile } from './google-user.js'; +import type { Env } from '../../config/env-schema.js'; +import type { GoogleUserProfile } from './google-user.service.js'; const GOOGLE_ISSUER = new URL('https://accounts.google.com'); const DEFAULT_RETURN_PATH = '/'; diff --git a/apps/api/src/auth/google-user.test.ts b/apps/api/src/auth/google/google-user.service.test.ts similarity index 96% rename from apps/api/src/auth/google-user.test.ts rename to apps/api/src/auth/google/google-user.service.test.ts index 241061a..1815195 100644 --- a/apps/api/src/auth/google-user.test.ts +++ b/apps/api/src/auth/google/google-user.service.test.ts @@ -2,7 +2,7 @@ import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { describe, expect, it, vi } from 'vitest'; -import { provisionGoogleUser } from './google-user.js'; +import { provisionGoogleUser } from './google-user.service.js'; function createDatabaseResult(rows: Array<{ id: string }>) { const returning = vi.fn(() => Promise.resolve(rows)); diff --git a/apps/api/src/auth/google-user.ts b/apps/api/src/auth/google/google-user.service.ts similarity index 95% rename from apps/api/src/auth/google-user.ts rename to apps/api/src/auth/google/google-user.service.ts index 40416b9..f58edc3 100644 --- a/apps/api/src/auth/google-user.ts +++ b/apps/api/src/auth/google/google-user.service.ts @@ -1,7 +1,7 @@ /** Atomically provisions or updates a user identified by Google's stable subject. */ import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; -import { users } from '../db/schema/index.js'; +import { users } from '../../db/schema/index.js'; export type GoogleUserProfile = { avatarUrl?: string; diff --git a/apps/api/src/auth/session.test.ts b/apps/api/src/auth/session/session.middleware.test.ts similarity index 98% rename from apps/api/src/auth/session.test.ts rename to apps/api/src/auth/session/session.middleware.test.ts index 58b482b..b616388 100644 --- a/apps/api/src/auth/session.test.ts +++ b/apps/api/src/auth/session/session.middleware.test.ts @@ -4,7 +4,7 @@ import session from 'express-session'; import request from 'supertest'; import { describe, expect, it } from 'vitest'; -import { createSessionMiddleware } from './session.js'; +import { createSessionMiddleware } from './session.middleware.js'; const sessionConfiguration = { SESSION_SECRET: 'test-session-secret-at-least-32-characters', diff --git a/apps/api/src/auth/session.ts b/apps/api/src/auth/session/session.middleware.ts similarity index 96% rename from apps/api/src/auth/session.ts rename to apps/api/src/auth/session/session.middleware.ts index 1720fee..e67c23c 100644 --- a/apps/api/src/auth/session.ts +++ b/apps/api/src/auth/session/session.middleware.ts @@ -3,7 +3,7 @@ import connectPgSimple from 'connect-pg-simple'; import session, { type Store } from 'express-session'; import type { Pool } from 'pg'; -import type { Env } from '../config/env-schema.js'; +import type { Env } from '../../config/env-schema.js'; declare module 'express-session' { interface SessionData { diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 92c0221..57a304c 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -5,9 +5,9 @@ import { createApp } from './app.js'; import { createGoogleOidcRouter, discoverGoogleOidc, -} from './auth/google-oidc.js'; -import { provisionGoogleUser } from './auth/google-user.js'; -import { createPostgresSession } from './auth/session.js'; +} from './auth/google/google-oidc.routes.js'; +import { provisionGoogleUser } from './auth/google/google-user.service.js'; +import { createPostgresSession } from './auth/session/session.middleware.js'; import { env } from './config/env.js'; import { createDatabase } from './db/client.js'; diff --git a/docs/architecture.md b/docs/architecture.md index bdd793a..a512ba2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -417,6 +417,11 @@ A conceptual feature organization might be: apps/api/src/ features/ auth/ + google/ + google-oidc.routes.ts + google-user.service.ts + session/ + session.middleware.ts exercises/ routines/ workouts/ @@ -437,6 +442,12 @@ Each backend feature may contain the pieces needed by that feature, such as: Do not force every feature to contain every layer. +Use descriptive role suffixes such as `.routes.ts`, `.service.ts`, +`.middleware.ts`, and `.schema.ts` where they clarify responsibility. Colocate +tests as `..test.ts`. Prefer focused capability names over broad +files such as `auth.service.ts`, and introduce controllers or other layers only +when their separation provides real value. + # 17. Backend Separation of Concerns