Clean Architecture in JavaScript: Mastering Hexagonal in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Your Code is a Fortress: Why Hexagonal Architecture Will Save Your JavaScript Project

\n\n

Have you ever faced a situation where changing a database or replacing an external API turned into a nightmare of rewriting half the project? Or perhaps you've written unit tests that felt more like integration tests because the business logic was tightly coupled to Express or MongoDB? If so — welcome to the world of Hexagonal Architecture (Ports and Adapters).

\n\n

This is not just a trendy pattern from Alistair Cockburn's books. It's a practical way to make your JavaScript code robust, testable, and framework-independent. In this article, we won't give boring lectures. We'll take a real task (an order management system) and build it step by step using the principles of hexagonal architecture in JavaScript. You'll see how the business logic remains pristine, while all the "noise" (databases, HTTP requests, queues) stays outside.

\n\n

Ready? Let's go!

\n\n

What is Hexagonal Architecture (and What Do Ports and Adapters Have to Do With It)?

\n\n

Imagine a spaceship gateway. Inside — a control panel (your business logic). Outside — an infinite number of different planets (databases, web frameworks, external services). The gateway is a port (interface). And the spacesuit that lets you breathe on a specific planet is an adapter.

\n\n

The core idea: the application core should not know who is knocking or how. It only declares "ports" (interfaces). And the concrete implementations (adapters) are plugged in from the outside. This gives three superpowers:

\n\n
    \n
  • Business logic isolation — you change the database without touching the discount calculation code.
  • \n
  • Testability — you test the logic without spinning up Docker or a real database.
  • \n
  • Framework independence — you can migrate from Express to Fastify by simply writing a new adapter.
  • \n
\n\n

Let's move on to the code.

\n\n

Project Setup and Folder Structure

\n\n

First, let's create a simple structure. We'll write in plain TypeScript (or JavaScript with JSDoc — whichever is more convenient). Our goal is an order management system.

\n\n
src/\n├── core/\n│   ├── domain/          # Entities and Value Objects\n│   ├── ports/           # Interfaces (ports)\n│   └── services/        # Business logic (use cases)\n├── adapters/\n│   ├── primary/         # Adapters that call our core (HTTP, CLI)\n│   └── secondary/       # Adapters that the core calls (DB, external APIs)\n└── app.js               # Entry point (wiring everything together)\n
\n\n

Step 1. Core: Domain Entities and Ports

\n\n

Let's start with the most important part — something that depends on nothing. We'll create the Order entity.

\n\n

core/domain/Order.js

\n\n
export class Order {\n  constructor({ id, userId, items, status = 'pending' }) {\n    this.id = id;\n    this.userId = userId;\n    this.items = items; // Array of { productId, quantity, price }\n    this.status = status;\n    this.total = this.calculateTotal();\n  }\n\n  calculateTotal() {\n    return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n  }\n\n  approve() {\n    if (this.status !== 'pending') {\n      throw new Error('Only pending orders can be approved');\n    }\n    this.status = 'approved';\n  }\n}\n
\n\n

Now let's define the ports — interfaces that our core expects from the outside world.

\n\n

core/ports/OrderRepository.js

\n\n
// This is a port (interface). We don't implement it here.\nexport class OrderRepository {\n  async save(order) { throw new Error('Not implemented'); }\n  async findById(id) { throw new Error('Not implemented'); }\n}\n
\n\n

core/ports/EmailService.js

\n\n
export class EmailService {\n  async sendOrderConfirmation(order) { throw new Err                

Blogs

Book Recommendations