Skip to main content

Getting Started

Let's start using hexagonal-react!


πŸ”Ή Prerequisites​

  • Node.js 18+
  • pnpm 8+

πŸ”Ή Installation​

Clone the template using

npx @dxbox/create-hexagonal-react your-app-name

πŸ”Ή Running the Development Server​

Start both Next.js and Vite example apps:

pnpm dev

This will run:

Both apps share the same domain, ports, use-cases, and adaptersβ€”demonstrating how you can use different frameworks with the same business logic.


πŸ”Ή Project Structure​

The monorepo is organized by architecture layers:

hexagonal-react/
β”œβ”€β”€ apps/
β”‚ β”œβ”€β”€ nextjs/ # Next.js 15 application
β”‚ └── vite/ # Vite 5 application
β”œβ”€β”€ packages/
β”‚ β”œβ”€β”€ domain/ # Business entities and commands (zero dependencies)
β”‚ β”œβ”€β”€ ports/ # Interfaces for external dependencies
β”‚ β”œβ”€β”€ use-cases/ # Application handlers
β”‚ β”œβ”€β”€ adapter-*/ # Concrete implementations (in-memory, API, ViewModel, etc.)
β”‚ └── ui/ # Pure React components
└── turbo.json # Turborepo configuration

πŸ”Ή Understanding the Demo Module​

The template includes a Task Manager demo module to illustrate the architecture. Explore it to understand how layers interact:

  • Domain: packages/domain/src/demo/
  • Ports: packages/ports/src/demo/
  • Use-cases: packages/use-cases/src/demo/
  • Adapters: packages/adapter-*/src/demo/
  • UI: packages/ui/src/demo/

Once you understand the pattern, you can safely remove the demo with

pnpm remove:demo

πŸ”Ή Architectural Rules Enforced by ESLint​

  • Domain cannot import anything
  • Ports can only import from Domain
  • Use-cases can only import from Domain and Ports
  • Adapters cannot cross-import or import UI
  • Apps (composition roots) are the only place that can import adapters

If you violate these rules, ESLint will fail the build. For example:

// ❌ This will FAIL to compile
import { InMemoryTaskRepository } from '@repo/adapter-in-memory';

export class CreateTaskHandler {
constructor(private repository: InMemoryTaskRepository) {} // ❌ Adapter imported in use-case
}
// βœ… This will compile
import type { TaskRepository } from '@repo/ports';

export class CreateTaskHandler {
constructor(private repository: TaskRepository) {} // βœ… Port imported in use-case
}

πŸ”Ή Next Steps​

Continue to the Workflow Guide to learn how to build features using hexagonal architecture.