Olympus Docs
InternalsAthena

Athena, feature modules

How features are organized in athena/src/features/

Athena's src/features/ directory holds the per-feature code. Each feature is roughly: a Next.js page, the components that make up the page, and the actions or API calls.

The 12 modules

ModulePageResponsibility
analytics/analyticsDashboard widgets, PKCE, MFA, login attempts
auth/login, /logout, /callbackAthena's own OAuth2 session against IAM Hera
identities/identities, /identities/[id]Identity CRUD UI
m2m-clients/m2m-clientsM2M OAuth2 client management
messages/messagesKratos courier message log
oauth2-auth(background)OAuth2 flow into Athena
oauth2-clients/oauth2-clients, /oauth2-clients/[id]User-facing OAuth2 client UI
oauth2-tokens/oauth2-tokensToken introspection tool
schemas/schemasIdentity schema editor + live reload
security/securitySecurity audit log viewer
sessions/sessionsActive sessions list + revoke
settings/settingsSettings vault editor

Per-module shape

A typical feature module:

src/features/<module>/
├── page.tsx              # Next.js page (the actual route)
├── components/           # Feature-specific React
│   ├── identity-form.tsx
│   ├── identity-list.tsx
│   └── ...
├── actions.ts            # Server actions / API client calls
├── types.ts              # Module-specific types
└── tests/                # Vitest tests

Pages import only from their own components/ and from app-wide lib/, services/, @olympusoss/canvas.

Cross-module sharing happens through lib/ (utilities), not via inter-module imports.

Why this structure

Three goals:

  1. Discoverability, "I want to edit the locked-accounts page" → src/features/security/.
  2. Encapsulation, changes to identity UI don't touch sessions UI.
  3. Testability, each module's tests live with the module.

Page routing

Next.js's App Router uses filesystem-based routing:

  • src/app/identities/page.tsx/identities
  • src/app/identities/[id]/page.tsx/identities/<uuid>

The pages in src/app/ are typically thin re-exports of feature module pages:

// src/app/identities/page.tsx
export { default } from "@/features/identities/page";

This keeps the App Router happy while feature code lives in the structured features/ tree.

Server actions

Athena uses Next.js server actions where appropriate:

// src/features/identities/actions.ts
"use server";
import { listIdentities } from "@/services/kratos/identities";

export async function getIdentities(domain: "ciam" | "iam") {
  return await listIdentities(domain, { pageSize: 50 });
}

Server actions run on the server, can call services directly, and surface results to client components via standard React patterns.

Where business logic lives

  • services/, Ory adapters.
  • lib/, cross-cutting utilities.
  • features/<x>/actions.ts, feature-specific server logic.
  • features/<x>/components/, feature-specific UI.

Business logic that's tightly coupled to one feature stays in that feature. Business logic that's used by multiple features goes to lib/.

Tests

features/identities/
├── actions.test.ts       # tests server actions, mocks services/kratos/identities
└── components/
    └── identity-form.test.tsx

Test naming convention: <file>.test.ts next to the code it tests.

Anti-patterns

  • Importing across feature boundaries (features/identities importing from features/sessions). If you need to do this, the shared code belongs in lib/.
  • Putting routes in app/ without a feature module. Every page should have a corresponding feature module home.
  • Mixing client components into app/ page files. Use feature module re-exports.

On this page