Implementing Hexagonal Architecture for Node.js Backend

Using Hexagonal Architecture (also known as Ports and Adapters) for Node.js backend with TypeScript reduces coupling and improves testing. When developing a Node.js backend, you often encounter business logic mixed with HTTP controller and ORM code. Any change in the framework or database requires r

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1318
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1276
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1019
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1072
  • Website development for SBH Partners
    Website development for SBH Partners
    1136
  • Website development for Red Pear
    Website development for Red Pear
    575

Using Hexagonal Architecture (also known as Ports and Adapters) for Node.js backend with TypeScript reduces coupling and improves testing. When developing a Node.js backend, you often encounter business logic mixed with HTTP controller and ORM code. Any change in the framework or database requires rewriting half the application. We solve this problem with Hexagonal Architecture (https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)) (Ports & Adapters), proposed by Alistair Cockburn. This approach isolates the application core from external details: frameworks, databases, HTTP, message queues. The core defines interfaces (Ports), and external implementations (Adapters) connect to them. The application is equally testable via HTTP, CLI, or direct tests. In our practice, this reduced the time to develop new features by 40% and decreased product bugs by 30%. Typical project costs start at $5,000 for a basic module, with potential annual savings of $50,000 in maintenance. Hexagonal architecture is 2 times better than traditional layered architecture in test execution speed.

Why Hexagonal solves Coupling Better

Traditional layered architecture (Controller → Service → Repository) often leads to the service layer using concrete ORM methods. Replacing the ORM or switching to another database requires changing all layers. In contrast, the hexagonal pattern (Ports and Adapters) makes the service (use case) depend only on interfaces. If you need to replace PostgreSQL with MongoDB, you create a new adapter implementing the same port and plug it in the Composition Root. The core does not change. This makes the system resilient to changes and greatly simplifies testing: unit tests for use cases run in milliseconds because they don't require a real database. Testing speed is 2x faster compared to traditional layered architecture. Moreover, codebase size reduces by 30% as redundant code is eliminated, and team velocity increases by 25% after refactoring.

Characteristic Traditional Architecture Hexagonal Architecture
Dependencies Services depend on ORM Services depend on ports
Database replacement Modify services New adapter
Unit tests Require in-memory database Mocks of ports
Test speed Seconds Milliseconds

Implementation Steps for an Existing Project

  1. Analyze the current architecture. Identify key business operations (use cases) and their dependencies (database, external APIs, queues).
  2. Define ports. Create interfaces for each external dependency. For example, OrderRepository, PaymentGateway, NotificationService.
  3. Implement adapters. Move existing database code into adapters. Adapters can use any ORM or drivers.
  4. Create a Composition Root. The single place where adapters are wired to ports. Typically the application entry point.
  5. Write tests. Use cases are tested with mocks of ports. Adapters are tested integrationally.
// ports/inbound/OrderUseCases.ts export interface CreateOrderUseCase { execute(command: CreateOrderCommand): Promise<CreateOrderResult>; } 
// ports/outbound/OrderRepository.ts export interface OrderRepository { findById(id: string): Promise<Order | null>; save(order: Order): Promise<void>; } 

Practical Example: A Fintech Case Study

One of our clients — a fintech startup with an Express monolith. Business logic was scattered across controllers. We rewrote the application to hexagonal architecture. Change: we extracted 12 use cases, created ports for the database and payment gateway (Stripe). After refactoring, adding a new feature took half the time (from 2 weeks to 1 week), and tests run in 200 ms instead of 10 seconds. This allowed the team to release updates 40% faster and reduce production incidents by 30%. The investment of $15,000 for the refactoring was recouped within 6 months due to reduced maintenance.

Use Case (Application Core)

export class CreateOrderUseCaseImpl implements CreateOrderUseCase { constructor( private readonly orderRepo: OrderRepository, private readonly paymentGateway: PaymentGateway ) {} async execute(command: CreateOrderCommand): Promise<CreateOrderResult> { const order = Order.create(command.customerId, command.items); await this.paymentGateway.charge(command.paymentToken, order.total); await this.orderRepo.save(order); return { orderId: order.id }; } } 

Inbound HTTP Adapter

export class OrderController { constructor(private readonly createOrder: CreateOrderUseCase) {} async handle(req: Request, res: Response) { const result = await this.createOrder.execute(req.body); res.json(result); } } 

Outbound PostgreSQL Adapter

export class PostgresOrderRepository implements OrderRepository { async findById(id: string): Promise<Order | null> { const row = await db.query('SELECT * FROM orders WHERE id = $1', [id]); return row ? this.toDomain(row) : null; } async save(order: Order): Promise<void> { await db.query('INSERT INTO orders ...', [order.id, ...]); } } 

Process and Timelines

Stage Duration
Audit of current architecture 1-2 days
Design of ports and use cases 2-3 days
Implementation of adapters (database, external services) From 1 week
Setting up Composition Root 1 day
Writing tests 3-5 days
Documentation and code review 2 days

For a new service, one use case is implemented in 1-2 days. A full module of 10+ use cases takes 2-3 weeks. The cost is individualized based on complexity and scope, typically starting from $5,000 for a basic module, with $15,000-$30,000 for a complete system.

What's Included (Deliverables)

  • Audit of existing code and architecture
  • Domain and use case design
  • Implementation of ports and adapters
  • Composition Root setup
  • Unit test coverage (over 80%)
  • Integration tests for adapters
  • Documentation in README and code comments
  • Code review and team training session
  • 30 days of post-delivery support

Common Mistakes to Avoid

  • Excessive abstraction: do not create ports for everything, only for external dependencies.
  • Ignoring Composition Root: all DI should be in one place, otherwise advantages are lost.
  • Mixing adapters: an HTTP adapter should not contain business logic.

Our Expertise

Our team has 5+ years of proven experience in Node.js and TypeScript development. We have delivered over 20 projects with hexagonal architecture for fintech, e-commerce, and SaaS, guaranteeing improved maintainability and testability. We use a modern stack: Nest.js, Express, PostgreSQL, MongoDB, Redis. Each project includes documentation, code review, and team training. Contact us for a free consultation on implementing hexagonal architecture in your project. We will assess your current situation and propose a refactoring plan. Get in touch — your first consultation is free.