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:
- Next.js app on http://localhost:3001
- Vite app on http://localhost:3002
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.