Skip to content

Subslices

A subslice is a slice nested inside a parent folder that acts as a namespace for a group of closely related features. Subslices let you organize a cohesive domain — such as user identity — into multiple focused slices without flattening them into the top-level slices/ directory.

What Subslices Are

Subslices are not a new pattern or a new layer. A subslice is a regular slice. The only difference is its location on disk: instead of living directly under slices/, it lives under a parent folder that groups related slices together.

slices/
├── user/                         # Parent folder (namespace only — no code of its own)
│   ├── user/                     # Subslice: profile, accounts, roles
│   │   ├── user.module.ts
│   │   ├── user.controller.ts
│   │   ├── user.prisma
│   │   ├── domain/
│   │   ├── data/
│   │   └── dtos/
│   │
│   ├── auth/                     # Subslice: login, JWT, guards
│   │   ├── auth.module.ts
│   │   ├── auth.controller.ts
│   │   ├── domain/
│   │   ├── dtos/
│   │   └── guards/
│   │
│   └── apiKey/                   # Subslice: API key issuance + scopes
│       ├── apiKey.module.ts
│       ├── apiKey.controller.ts
│       ├── apiKey.prisma
│       ├── domain/
│       ├── data/
│       └── dtos/

└── project/                      # A regular top-level slice (no subslices)
    ├── project.module.ts
    └── ...

Each subslice keeps the full slice anatomy: its own module, controller, domain/, data/, and dtos/. The parent folder (user/) is purely a namespace — it has no user.module.ts at its root, no controller, no domain code. It exists only to group its children on disk.

When to Use Subslices

Reach for subslices when several slices belong to one bounded context and would otherwise pollute the top-level slices/ directory.

Good candidates:

ParentSubslicesWhy grouped
user/user/, auth/, apiKey/All concern user identity
billing/subscription/, invoice/, payment/All concern monetary flows
content/post/, comment/, reaction/All concern published content

Stay with flat top-level slices when:

  • A feature stands alone (no natural sibling features).
  • You only have one slice in the group — there is nothing to namespace yet.
  • Slices in the group have no shared domain language; grouping them would be artificial.

TIP

Subslices are an organizational convenience, not an architectural requirement. Start flat. Promote a group of slices into a subslice folder only when the top-level directory genuinely needs the structure.

Real Example: The user/ Group

The Ranch API groups three slices under api/src/slices/user/:

api/src/slices/user/
├── user/        # The user entity, profile, roles
├── auth/        # Authentication, JWT, guards, login/register
└── apiKey/      # Programmatic API keys with scopes

Each child is registered independently in app.module.ts — there is no aggregating module at user/:

typescript
// api/src/app.module.ts
import { UserModule } from './slices/user/user/user.module';
import { AuthModule } from './slices/user/auth/auth.module';
import { ApiKeyModule } from './slices/user/apiKey/apiKey.module';

@Module({
  imports: [
    // Setup slices...
    UserModule,
    AuthModule,
    ApiKeyModule,
    // Other feature slices...
  ],
})
export class AppModule {}

Each subslice module looks exactly like a normal slice module:

typescript
// slices/user/user/user.module.ts
@Module({
  imports: [forwardRef(() => AuthModule)],
  controllers: [UserController],
  providers: [
    UserMapper,
    { provide: IUserGateway, useClass: UserGateway },
  ],
  exports: [IUserGateway, UserMapper],
})
export class UserModule {}

Naming Rules

Subslices follow the same naming rules as top-level slices:

WhatRuleExample
Parent folderSingular, no suffixuser/, not users/ or userGroup/
Subslice folderSingularauth/, apiKey/
Subslice module{name}.module.tsauth.module.ts
Subslice controller@Controller('{plural}')@Controller('auth'), @Controller('api-keys')

A subslice may share the parent's name (user/user/). This is intentional and common: the "main" slice in the group keeps the parent name to signal that it is the primary domain owner.

Cross-Subslice Communication

Subslices in the same parent group can — and often do — depend on each other. The rules are the same as for any other slice-to-slice communication: go through the domain public interface, never reach into another slice's data/ folder.

Allowed

typescript
// slices/user/apiKey/apiKey.module.ts — imports the auth subslice
import { AuthModule } from '#/user/auth/auth.module';

@Module({
  imports: [forwardRef(() => AuthModule)],
  // ...
})
export class ApiKeyModule {}
typescript
// slices/user/auth/auth.controller.ts — uses user domain types
import { IUserData } from '#/user/user/domain';

Not Allowed

typescript
// Reaching into another subslice's data layer — same rule as top-level slices
import { UserGateway } from '#/user/user/data/user.gateway'; // wrong

Circular Imports

When two subslices in a group reference each other (authapiKey is common), use forwardRef() the same way you would between any two NestJS modules. The grouping does not change the resolution rules — it only changes the import path.

What the Parent Folder Does Not Contain

A common mistake is treating the parent folder as a slice itself. It is not.

Do not add to the parent folder:

  • user.module.ts aggregating child modules
  • user.controller.ts
  • A domain/ or data/ folder
  • Shared types — those belong in whichever subslice owns them, exported via that subslice's domain/index.ts

If you find yourself wanting shared code at the parent level, that is a signal you need a new subslice (for example, user/common/) rather than code in the namespace folder itself.

Frontend Subslices

The same pattern applies on the frontend. Each subslice is its own Nuxt layer, registered individually in the root nuxt.config.ts:

typescript
// app/nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    // Setup slices...
    './slices/user/user',
    './slices/user/auth',
    './slices/user/apiKey',
    // ...
  ],
});

Pinia stores, components, locales, and pages from each subslice are auto-imported through the layer system just like top-level slices.

Summary

  • A subslice is a regular slice nested under a namespace folder.
  • The parent folder owns no code — it only groups its children.
  • Each subslice retains the full three-layer anatomy and is registered independently.
  • Use subslices when several slices share a bounded context; stay flat otherwise.
  • All slice rules — singular folder names, gateway pattern, no cross-data imports — apply unchanged.

What's Next?

Built with CleanSlice