Sébastien TIMONER
Expert in web development and team management, I specialize in creating and optimizing high-performance digital solutions. With extensive expertise in modern technologies like React.js, Node.js, TypeScript, Symfony, and Zephyr OS for IoT, I ensure the success of complex SaaS and IoT projects, from design to production, for companies across various sectors, at offroadLabs.
At offroadLabs, I offer custom development services that combine technical expertise with a collaborative approach. Whether creating an innovative SaaS solution, developing IoT systems with Zephyr OS, modernizing an existing application, or supporting the upskilling of a team, I am committed to delivering robust and high-performance solutions tailored to the specific needs of each project.
I am available for projects in the Aix-en-Provence area or fully remote.
How to structure a modern web application? How to ensure it remains maintainable over time? In this guide, we'll create a Next.js application using hexagonal architecture and robust form validation. To make learning more fun, we'll create a Jedi Academy registration system!
The source code is available on GitHub.
Hexagonal Architecture, also known as "Ports and Adapters", is an architectural pattern that allows creating applications where business components are:
The architecture is divided into three main layers:
[Content continues...]
bash
When creating the project, answer the following questions:
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? Yes
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias (@/*)? Yes
Using Next.js version 15.0.0+ with Turbopack
Once the project is created, install the dependencies:
bash
When initializing shadcn/ui, answer the following questions:
Would you like to use TypeScript (recommended)? Yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › src/app/globals.css
Would you like to use CSS variables for colors? › Yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › Yes
# Install necessary components
npx shadcn@latest add form input select textarea button card toast table
bash
src/
├── app/
│ ├── actions/
│ │ └── register-padawan.ts
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ ├── jedi-form.tsx
│ │ └── padawan-list.tsx
│ ├── lib/
│ │ └── utils.ts # shadcn/ui utilities
│ └── page.tsx
├── core/
│ ├── domain/
│ │ ├── models/
│ │ │ └── padawan.ts
│ │ ├── ports/
│ │ │ └── padawan-repository.ts
│ │ ├── services/
│ │ │ └── padawan-eligibility.ts
│ │ ├── validation/
│ │ │ └── padawan-schema.ts
│ │ └── events/
│ │ └── padawan-registered.ts
│ └── usecases/
│ └── register-padawan.ts
└── infrastructure/
└── db/
├── prisma/
│ ├── migrations/
│ └── schema.prisma
├── prisma.ts
└── repositories/
└── prisma-padawan-repository.ts
This structure follows a clear hexagonal architecture with:
app/
- Next.js user interface layeractions/
- Server Actions for form handlingcomponents/
- React components with UI/business separationlib/
- Shared utilitiescore/
- Pure business logicdomain/
- Models, ports, and business servicesusecases/
- Application use casesinfrastructure/
- Technical implementationsdb/
- Prisma configuration and repository implementationsThe domain is the heart of our application. This is where we translate business rules into code, independently of any technical consideration. In our case, the domain represents all the rules governing registration at the Jedi Academy.
Models represent fundamental business concepts.
typescript
Events represent important facts that have occurred in the domain.
typescript
Ports define how the domain interacts with the outside world.
typescript
Domain services encapsulate business logic that doesn't naturally fit into an entity.
typescript
Use cases orchestrate interactions between different domain elements to achieve a business functionality.
typescript
The infrastructure implements the ports defined in the domain to interact with the outside world.
prisma
typescript
Input data validation uses Zod to ensure consistency with our domain.
typescript
Now we can generate the db.
bash
For further exploration, Prisma provides a client to visualize the db.
bash
typescript
typescript
typescript
These components use shadcn/ui to create a modern and accessible user interface, with:
Everything integrates perfectly into our hexagonal architecture while staying at the presentation layer, without business logic.
typescript
typescript
Hexagonal architecture has allowed us to create an application that is:
To go further, you could:
Remember: good architecture is like the Force - it must be in balance! 🌟
[End of translation]