Skip to content

Presets

Stricture provides battle-tested presets for popular architectural patterns. Each preset comes with pre-configured boundaries and rules that enforce best practices out of the box.

What Are Presets?

Presets are pre-packaged architectural configurations that include:

  • Boundary definitions - File patterns that define architectural layers
  • Enforcement rules - Dependency constraints between layers
  • Best practices - Battle-tested patterns used in production

Instead of manually defining boundaries and rules, use a preset and get started immediately:

eslint.config.js
import stricture from '@stricture/eslint-plugin'
export default [
stricture.configs.hexagonal() // That's it!
]

Installation

All presets are bundled in @stricture/eslint-plugin. Install once, use any preset:

Terminal window
npm install -D @stricture/eslint-plugin
%%{init: {'theme':'base'}}%%
graph LR
    User["Your Project"]
    Plugin["@stricture/eslint-plugin
includes all presets"] Core["@stricture/core"] User -->|installs| Plugin Plugin -->|depends on| Core style User fill:#e3f2fd style Plugin fill:#fff3e0 style Core fill:#e8f5e9

Usage

Use any bundled preset in your ESLint config:

eslint.config.js
import stricture from '@stricture/eslint-plugin'
// Hexagonal architecture
export default [stricture.configs.hexagonal()]
// Next.js patterns
export default [stricture.configs.nextjs()]
// Layered architecture
export default [stricture.configs.layered()]

No additional packages needed!

Why Use Presets?

Zero Configuration

Presets work out-of-the-box with common project structures. No need to define boundaries or rules manually.

Battle-Tested Patterns

Each preset embodies proven architectural patterns used successfully in production applications.

Consistency

Using presets ensures consistent architecture across your organization’s projects.

Learning Tool

Presets serve as reference implementations, helping teams learn architectural patterns through enforcement.

Available Presets

Hexagonal Architecture

Config: stricture.configs.hexagonal()

Domain-centric architecture with clear separation between business logic and adapters.

  • Best for: Complex domain logic, DDD projects
  • Complexity: Medium-High
  • Learning curve: Medium

Learn more →


Layered Architecture

Config: stricture.configs.layered()

Traditional N-tier architecture with presentation, application, domain, and infrastructure layers.

  • Best for: Traditional applications, established teams
  • Complexity: Low-Medium
  • Learning curve: Low

Learn more →


Clean Architecture

Config: stricture.configs.clean()

Uncle Bob’s Clean Architecture with concentric dependency circles.

  • Best for: Enterprise applications, strict dependency control
  • Complexity: Medium-High
  • Learning curve: Medium

Learn more →


Modular Architecture

Config: stricture.configs.modular()

Feature-based modules with public API boundaries and encapsulation.

  • Best for: Large applications, multiple teams
  • Complexity: Medium
  • Learning curve: Low-Medium

Learn more →


Next.js Patterns

Config: stricture.configs.nextjs()

Next.js App Router patterns with server/client boundaries.

  • Best for: Next.js applications, React Server Components
  • Complexity: Low
  • Learning curve: Low

Learn more →


NestJS Patterns

Config: stricture.configs.nestjs()

NestJS best practices with controller/service/repository layers.

  • Best for: NestJS applications, API backends
  • Complexity: Low-Medium
  • Learning curve: Low

Learn more →

Comparison Table

PresetConfigBest ForComplexityLearning CurveKey Benefit
Hexagonalconfigs.hexagonal()Complex domains, DDDMedium-HighMediumDomain isolation
Layeredconfigs.layered()Traditional appsLow-MediumLowFamiliar pattern
Cleanconfigs.clean()Enterprise appsMedium-HighMediumStrict dependencies
Modularconfigs.modular()Large teamsMediumLow-MediumModule encapsulation
Next.jsconfigs.nextjs()React SSR appsLowLowFramework-specific
NestJSconfigs.nestjs()API backendsLow-MediumLowFramework-specific

How to Choose?

Choose Hexagonal if you have:

  • Complex business rules
  • Domain-Driven Design approach
  • Need for adapter swappability
  • Multiple entry points (CLI, HTTP, GraphQL)

Choose Layered if you have:

  • Traditional application structure
  • Team familiar with N-tier patterns
  • Straightforward business logic
  • Need for simplicity

Choose Clean if you have:

  • Enterprise application requirements
  • Strict dependency control needs
  • Framework independence as priority
  • Long-term maintainability focus

Choose Modular if you have:

  • Large application
  • Multiple teams working on features
  • Need for module isolation
  • Feature-based organization

Choose Next.js if you have:

  • Next.js App Router application
  • Server/Client Component separation needs
  • Server Actions usage
  • Next.js-specific patterns

Choose NestJS if you have:

  • NestJS application
  • API backend structure
  • Dependency injection patterns
  • TypeScript decorators

Customization

Add custom rules directly in your ESLint config:

eslint.config.js
import stricture from '@stricture/eslint-plugin'
export default [
stricture.configs.hexagonal({
rules: [
{
id: 'all-to-shared',
from: { tag: '*' },
to: { pattern: 'src/shared/**' },
allowed: true
}
]
})
]

File-Based Customization (Alternative)

For complex configurations, use .stricture/config.json:

.stricture/config.json
{
"preset": "@stricture/hexagonal",
"boundaries": [
{
"name": "shared",
"pattern": "src/shared/**",
"mode": "file",
"tags": ["shared"]
}
],
"rules": [
{
"id": "all-to-shared",
"from": { "tag": "*" },
"to": { "tag": "shared" },
"allowed": true
}
]
}
eslint.config.js
export default [
stricture.configs.recommended() // Loads .stricture/config.json
]

Adapting Presets to Existing Folder Structures

Already have hexagonal, layered, or clean architecture but with different folder names? No problem! You can use the preset’s rules while adapting boundaries to match your existing structure.

The Problem

Presets expect specific folder structures:

PresetExpected StructureYour Structure (Example)
Hexagonalsrc/core/domain/
src/adapters/driving/
src/domain/
src/infrastructure/http/
Layeredsrc/presentation/
src/business/
src/controllers/
src/services/
Cleansrc/entities/
src/use-cases/
src/domain/
src/application/

The Solution

Override the preset’s boundaries to match your folders. The preset’s rules stay the same - they reference boundary tags, not folder patterns.

Example: Hexagonal with Custom Folders

Your existing structure:

src/
├── domain/ # Instead of src/core/domain/
├── ports/ # Instead of src/core/ports/
├── application/ # Instead of src/core/application/
└── infrastructure/
├── http/ # Instead of src/adapters/driving/
└── database/ # Instead of src/adapters/driven/

Configuration:

.stricture/config.json
{
"preset": "@stricture/hexagonal",
"boundaries": [
{
"name": "domain",
"pattern": "src/domain/**",
"mode": "file",
"tags": ["domain", "core"]
},
{
"name": "ports",
"pattern": "src/ports/**",
"mode": "file",
"tags": ["ports", "core"]
},
{
"name": "application",
"pattern": "src/application/**",
"mode": "file",
"tags": ["application", "core"]
},
{
"name": "http-adapters",
"pattern": "src/infrastructure/http/**",
"mode": "file",
"tags": ["driving", "adapters"]
},
{
"name": "database-adapters",
"pattern": "src/infrastructure/database/**",
"mode": "file",
"tags": ["driven", "adapters"]
}
]
}

What happens:

  • ✅ Your boundaries replace the preset’s boundary patterns
  • ✅ The preset’s rules still work (they match on tags: domain, ports, driving, driven)
  • ✅ You get hexagonal architecture enforcement with your existing folders

Example: Layered with Different Names

Your existing structure:

src/
├── controllers/ # Instead of src/presentation/
├── services/ # Instead of src/business/
├── repositories/ # Instead of src/data/
└── config/ # Instead of src/infrastructure/

Configuration:

.stricture/config.json
{
"preset": "@stricture/layered",
"boundaries": [
{
"name": "controllers",
"pattern": "src/controllers/**",
"mode": "file",
"tags": ["presentation"]
},
{
"name": "services",
"pattern": "src/services/**",
"mode": "file",
"tags": ["business"]
},
{
"name": "repositories",
"pattern": "src/repositories/**",
"mode": "file",
"tags": ["data"]
},
{
"name": "config",
"pattern": "src/config/**",
"mode": "file",
"tags": ["infrastructure"]
}
]
}

Key Points

Important: Match the preset’s tags, not its folder patterns.

  • Each preset’s rules use specific tags (e.g., domain, ports, driving, driven)
  • When you override boundaries, use the same tags the preset expects
  • Check the preset’s documentation to see which tags it uses
  • Your boundary name can be anything - it’s the tags that matter

Finding Preset Tags

To see which tags a preset uses:

  1. Check the preset’s documentation page (e.g., Hexagonal Preset)
  2. Look at the “Boundaries” section in each preset’s docs
  3. Or inspect the preset source code in @stricture/eslint-plugin

When to Use This Approach

Use boundary overrides when:

  • ✅ You follow the architecture pattern (hexagonal, layered, etc.)
  • ✅ Your folder structure is different from the preset’s expectations
  • ✅ You want to keep the preset’s rules without rewriting them

Don’t use this if:

  • ❌ Your architecture is fundamentally different → Create a custom preset instead
  • ❌ You only need minor additions → Just add boundaries/rules without replacing all boundaries

Composing Multiple Presets

Extending Presets

You can compose multiple presets using the extends field. This is useful for:

  • Adding organizational standards to architecture presets
  • Combining framework-specific rules with architectural patterns
  • Layering security policies and code standards
.stricture/config.json
{
"preset": "@stricture/hexagonal",
"extends": [
"@company/code-standards",
"@company/security-policies"
]
}

Merge order: Base preset → Extended presets (left to right) → Your custom rules

Monorepo: Different Presets per Package

For monorepos where different packages need different architectures, configure each package separately:

packages/api/eslint.config.js
export default [stricture.configs.hexagonal()]
packages/web/eslint.config.js
export default [stricture.configs.nextjs()]

See the Monorepo Setup Guide for more details.

Creating Custom Presets

You can create organization-specific presets by following the same structure:

import type { ArchPreset } from '@stricture/core'
export const myPreset: ArchPreset = {
boundaries: [
// Your boundary definitions
],
rules: [
// Your rules
]
}

See the API Reference for details.

Next Steps

  • Explore individual preset documentation
  • Check out examples for real-world usage
  • Read the configuration guide to learn about customization