Skip to content

eg180/clean-architecture-hexagonal

Repository files navigation

Implementing Hexagonal Architecture in Node.js with TypeScript

Hexagonal Architecture

The purpose of this project was to practice working with clean code principles. This project implements Hexagonal Architecture using Typescript and NodeJS. The project utilizes Express, Fastify, and TypeORM as frameworks and tools to manage HTTP requests and database interactions on the edge layer.

Purpose of the Architecture

The Hexagonal Architecture, also known as Ports and Adapters architecture, aims to create a flexible and loosely coupled design. It achieves this by separating the core business logic (application and domain) from the external systems (databases, web frameworks, etc.). The design promotes testability, maintainability, and scalability of the application.

In this project:

  • The Core contains business logic that remains decoupled from the external layers.
  • The Infrastructure layer deals with the specifics of external integrations (e.g., persistence mechanisms, clients).
  • The Application layer is responsible for implementing use cases and orchestrating the flow of data.

Core Concepts of Hexagonal Architecture

  1. Entities (Domain Layer): Entities represent the core business objects of the system, such as Item, Order, and Payment. They encapsulate the business rules and logic but are free from any technical dependencies (such as web frameworks or databases).

    Example: Item.ts, Order.ts, and Payment.ts are entities that contain attributes and methods relevant to the system's business logic.

  2. Value Objects: Value objects are immutable objects that describe characteristics of our domain and are distinguished only by their attributes. Examples include Money, Payment, and OrderStatus.

  3. Ports: Ports define interfaces that decouple the core logic from external systems or services. They act as entry (input ports) or exit (output ports) points to/from the application. These are implemented in the application/ports/ directory for external services (e.g., PaymentService, CacheService) and in the core/ports/ directory for persistence (Repository.ts) or external clients.

  4. Adapters (Infrastructure Layer): Adapters implement the details of external interactions based on the ports defined in the application or core layer. For example, adapters for data persistence (repositories using TypeORM or in-memory storage) or clients for third-party services like payment gateways.

    Example:

    • TypeOrmItemRepository.ts and InMemoryItemRepository.ts are repository adapters for interacting with storage.
    • InMemoryPaymentClient.ts is an example of a payment client adapter.
  5. Application Services (Application Layer): These services define the main use cases of the application. They act as orchestrators, interacting with the domain entities and ports to fulfill business requirements. The application services reside in the services/ directory.

    Example:

    • ItemApplicationService.ts handles operations related to Item such as creating, updating, or fetching items.
    • OrderApplicationService.ts handles order-specific business logic.
  6. Controllers: Controllers are part of the edge layer and handle HTTP requests from external clients. They convert the incoming data to the format expected by the application and delegate business operations to the application services.

    Example:

    • ItemController.ts handles item-related HTTP routes and maps them to the corresponding application service.
    • OrderController.ts does the same for orders.

Key Design Decisions

  • Aggregates vs DTOs: Aggregates (e.g., Order, Item) are used in domain logic to enforce business rules, while DTOs are used for data transfer across boundaries.
  • Repository Pattern: A generic repository interface with multiple implementations (In-memory, TypeORM) for type-safe data access.
  • Dependency Injection: Constructor-based injection for loose coupling between components, making testing and maintenance easier.

Recent Enhancements

  • Payment Handling: The Payment value object now accepts both string and Money types for the amount, allowing for flexible input from external sources.
  • Order Validation: The Order aggregate processes incoming data to convert string amounts to Money objects, ensuring data integrity.
  • Improved Error Handling: Enhanced validation checks in the Order aggregate to ensure that amounts and payment structures are correct.

Default In-Memory Options

By default, the following in-memory implementations are used:

  • InMemoryItemRepository.ts for item persistence.
  • InMemoryOrderRepository.ts for order persistence.
  • InMemoryPaymentClient.ts for payment processing.

These can be replaced by their TypeORM counterparts if you want to integrate a real database.

Getting Started

Prerequisites

Make sure you have Docker and Docker-Compose installed for running the infrastructure services (e.g., databases).

Installation

Install all dependencies by running:

npm install

Running the Application

You can run the application using either Express or Fastify.

Using Express:

npm run start:express

Using Fastify:

npm run start:fastify

Running with Docker

To start the necessary infrastructure using Docker, you can run:

docker-compose up -d

Testing

The project includes unit tests for core services. You can run the tests with:

npm run test

Contributors

Your Name Erick Gonzalez | eg180 - Restructuring for clean code principles, Include implementation for Orders service, expand test coverage

Acknowledgments

This project is a fork of Hexagonal Architecture Example, created by guilhermegarcia86.

Significant changes have been made to better align with clean code principles, including project restructuring, implementation for Orders service, and expanding test coverage.

Future Improvements

  • Add event sourcing
  • Implement CQRS pattern
  • Add more validation rules
  • Enhance error handling
  • Add API documentation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Expanding on and learning from a great example

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •