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:
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:
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:
import stricture from '@stricture/eslint-plugin'
// Hexagonal architectureexport default [stricture.configs.hexagonal()]
// Next.js patternsexport default [stricture.configs.nextjs()]
// Layered architectureexport 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
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
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
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
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
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
Comparison Table
| Preset | Config | Best For | Complexity | Learning Curve | Key Benefit |
|---|---|---|---|---|---|
| Hexagonal | configs.hexagonal() | Complex domains, DDD | Medium-High | Medium | Domain isolation |
| Layered | configs.layered() | Traditional apps | Low-Medium | Low | Familiar pattern |
| Clean | configs.clean() | Enterprise apps | Medium-High | Medium | Strict dependencies |
| Modular | configs.modular() | Large teams | Medium | Low-Medium | Module encapsulation |
| Next.js | configs.nextjs() | React SSR apps | Low | Low | Framework-specific |
| NestJS | configs.nestjs() | API backends | Low-Medium | Low | Framework-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
Inline Customization (Recommended)
Add custom rules directly in your ESLint config:
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:
{ "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 } ]}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:
| Preset | Expected Structure | Your Structure (Example) |
|---|---|---|
| Hexagonal | src/core/domain/src/adapters/driving/ | src/domain/src/infrastructure/http/ |
| Layered | src/presentation/src/business/ | src/controllers/src/services/ |
| Clean | src/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:
{ "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:
{ "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
namecan be anything - it’s thetagsthat matter
Finding Preset Tags
To see which tags a preset uses:
- Check the preset’s documentation page (e.g., Hexagonal Preset)
- Look at the “Boundaries” section in each preset’s docs
- 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
{ "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:
export default [stricture.configs.hexagonal()]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