Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions architectures/hexagonal-architecture/data-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
description: Hexagonal Architecture Data Flow rules for AI agents and developers. Understanding execution sequences across Ports and Adapters.
technology: Hexagonal Architecture
domain: Architecture
level: Senior/Architect
version: Agnostic
tags: [best-practices, data-flow, hexagonal-architecture, ports-and-adapters]
ai_role: Senior Software Architect
last_updated: 2026-03-22
---

# 🔄 Hexagonal Architecture Data Flow Best Practices

<div align="center">
**Execution paths and communication between layers.**
</div>

---

## 🔁 The Sequence of Execution

In Hexagonal Architecture, a request from the outside world must pierce through the layers strictly via defined Interfaces (Ports).

```mermaid
sequenceDiagram
participant UI as Primary Adapter (Controller)
participant IP as Input Port (Interface)
participant Domain as Core Domain (Use Case)
participant OP as Output Port (Interface)
participant DB as Secondary Adapter (Repository)

UI->>IP: Invoke Execute(Command)
IP->>Domain: Transform Command to Domain Logic
Domain->>OP: Request Data (findById)
OP->>DB: Perform SQL/NoSQL Query
DB-->>OP: Return Entity State
OP-->>Domain: Return Entity
Domain->>Domain: Apply Business Invariants
Domain->>OP: Save New State (save)
OP->>DB: Persist Changes
DB-->>OP: Ack
OP-->>Domain: Success
Domain-->>IP: Return Result/DTO
IP-->>UI: Form HTTP/RPC Response
```

## ⛔ Boundary Constraints (Data Flow Rules)

1. **No External Imports in Domain:** The Core Domain must NEVER import code from an Adapter (e.g., `import { PostgresDB } from '../adapters/db'`). It only implements Interfaces.
2. **Adapter Injection:** Adapters are injected into the Domain (typically during app startup) via the Output Ports (Interfaces).
3. **Primary vs Secondary:**
- Primary Adapters (Driving) call the Domain (Input Ports). Examples: REST Controllers, CLI scripts, Event Listeners.
- Secondary Adapters (Driven) are called by the Domain (Output Ports). Examples: Database Repositories, SMTP Clients, External API clients.
4. **Data Translation:** Data must be mapped at the boundary. Do not pass the internal DB Model directly out to the Primary Adapter. Use DTOs at the Ports.
77 changes: 77 additions & 0 deletions architectures/hexagonal-architecture/folder-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
description: Hexagonal Architecture Folder Structure rules. Defining the exact directory blueprints for Ports and Adapters.
technology: Hexagonal Architecture
domain: Architecture
level: Senior/Architect
version: Agnostic
tags: [best-practices, folder-structure, hexagonal-architecture, ports-and-adapters]
ai_role: Senior Software Architect
last_updated: 2026-03-22
---

# 📁 Folder Structure Best Practices for Hexagonal Architecture

<div align="center">
**Strict directory blueprints for zero-approval AI parsing.**
</div>

---

## 🌳 The Root Hierarchy

A properly defined Hexagonal architecture clearly separates its concerns at the file-system level. AI Agents are expected to enforce this strict separation.

```mermaid
graph TD
Src[src/] --> Core[core/]
Src --> Adapters[adapters/]
Core --> Ports[ports/]
Core --> Domain[domain/]
Ports --> In[in/]
Ports --> Out[out/]
Adapters --> Primary[primary/]
Adapters --> Secondary[secondary/]

%% Design Token Styles for Mermaid Diagrams
classDef default fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,color:#000;
classDef component fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000;
classDef layout fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#000;

class Src component;
class Core component;
class Adapters component;
class Ports component;
class Domain component;
```

## 🏗️ Example Directory Content

```text
src/
├── 📁 core/ # The Heart of the System (No External Tech)
│ ├── 📁 domain/ # Entities, Value Objects, Business Rules
│ │ ├── User.ts
│ │ └── AccountId.ts
│ └── 📁 ports/ # Interfaces defining interactions
│ ├── 📁 in/ # Primary Ports (Use Cases / Commands)
│ │ └── CreateUserUseCase.ts
│ └── 📁 out/ # Secondary Ports (SPIs / Repositories)
│ ├── UserRepositoryPort.ts
│ └── EmailSenderPort.ts
└── 📁 adapters/ # Concrete implementations
├── 📁 primary/ # Entry Points (Driving Adapters)
│ ├── 📁 http/ # REST Controllers / Express Routes
│ │ └── UserController.ts
│ └── 📁 cli/ # Console Commands
└── 📁 secondary/ # Exit Points (Driven Adapters)
├── 📁 database/ # ORMs (TypeORM, Prisma)
│ └── PostgresUserRepository.ts
└── 📁 external/ # 3rd Party APIs (SendGrid, Stripe)
└── SendGridEmailSender.ts
```

## ⛔ Boundary Constraints

1. **Isolation in `core/`:** Code inside `core/` is forbidden from importing modules from `adapters/`.
2. **Implementation in `adapters/`:** Code inside `adapters/` relies heavily on implementing the interfaces declared in `core/ports/`.
3. **Primary vs Secondary File Naming:** Append descriptive suffixes to Adapters to clarify intent (e.g., `PostgresUserRepository`, `StripePaymentService`).
170 changes: 170 additions & 0 deletions architectures/hexagonal-architecture/implementation-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
description: Hexagonal Architecture Implementation Guide rules. Defining the exact code constraints for Vibe Coding and AI.
technology: Hexagonal Architecture
domain: Architecture
level: Senior/Architect
version: Agnostic
tags: [best-practices, implementation-guide, hexagonal-architecture, ports-and-adapters]
ai_role: Senior Software Architect
last_updated: 2026-03-22
---

# 🛠️ Hexagonal Architecture Implementation Guide

<div align="center">
**Executable blueprints and constraints for AI-agent code generation.**
</div>

---

## ⚡ The Vibe Coding Instructions (Constraints)

For any code generated within the Hexagonal Architecture ecosystem, the following boundaries must be strictly enforced:

1. **Core Isolation (No External Imports):**
Code inside `src/core/domain/` or `src/core/ports/` must have **ZERO** imports from `src/adapters/` or any external UI/DB frameworks.

2. **Strict Interface Fulfillment:**
Code in `src/adapters/secondary/` must implement interfaces declared in `src/core/ports/out/`. It acts as a bridge to external technologies (e.g., PostgreSQL, SendGrid).

3. **Boundary DTO Translation:**
Adapters must map raw external data (HTTP Requests, Database Rows) into pure Domain Entities before passing them inward. When returning data, Domain Entities must be translated back into Adapter-specific DTOs before reaching the outside world. Do not leak the Core Entity to an HTTP response directly.

---

## 💻 Concrete Code Examples

### 🧩 Entity Relationships (Class Diagram)

```mermaid
classDiagram
class User {
-String _id
-String _email
-String _status
+constructor(id, email)
+id() String
+email() String
+deactivate() void
}

class UserRepositoryPort {
<<interface>>
+findById(id: String) Promise~User~
+save(user: User) Promise~void~
}

class DeactivateUserUseCase {
-UserRepositoryPort userRepository
+execute(userId: String) Promise~void~
}

class PostgresUserRepository {
+findById(id: String) Promise~User~
+save(user: User) Promise~void~
}

DeactivateUserUseCase --> UserRepositoryPort : Uses
PostgresUserRepository ..|> UserRepositoryPort : Implements
UserRepositoryPort ..> User : Returns/Receives
```

### ✅ Best Practice: Core Domain (`src/core/domain/User.ts`)

```typescript
// Pure domain object - No framework annotations like @Entity or @Table
export class User {
private readonly _id: string;
private _email: string;
private _status: 'ACTIVE' | 'INACTIVE';

constructor(id: string, email: string) {
this._id = id;
this._email = email;
this._status = 'ACTIVE';
}

public get id(): string { return this._id; }
public get email(): string { return this._email; }

public deactivate(): void {
this._status = 'INACTIVE';
}
}
```

### ✅ Best Practice: Output Port (`src/core/ports/out/UserRepositoryPort.ts`)

```typescript
// Interface defined by the Domain to specify its needs from a DB
import { User } from '../../domain/User';

export interface UserRepositoryPort {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
}
```

### ✅ Best Practice: Secondary Adapter (`src/adapters/secondary/database/PostgresUserRepository.ts`)

```typescript
// Adapter implementing the Port using an ORM (e.g., TypeORM or Prisma)
import { UserRepositoryPort } from '../../../core/ports/out/UserRepositoryPort';
import { User } from '../../../core/domain/User';
import { db } from '../../../infrastructure/db'; // Imaginary DB client
import { UserMapper } from './UserMapper'; // Maps DB Row to Domain Entity

export class PostgresUserRepository implements UserRepositoryPort {
async findById(id: string): Promise<User | null> {
const rawRow = await db.query('SELECT * FROM users WHERE id = $1', [id]);
if (!rawRow) return null;
return UserMapper.toDomain(rawRow);
}

async save(user: User): Promise<void> {
await db.query('INSERT INTO users(id, email, status) VALUES($1, $2, $3)', [
user.id, user.email, user.status
]);
}
}
```

### ✅ Best Practice: Use Case / Input Port (`src/core/ports/in/DeactivateUserUseCase.ts`)

```typescript
import { UserRepositoryPort } from '../out/UserRepositoryPort';

export class DeactivateUserUseCase {
constructor(private readonly userRepository: UserRepositoryPort) {}

async execute(userId: string): Promise<void> {
const user = await this.userRepository.findById(userId);
if (!user) throw new Error('User not found');

user.deactivate(); // Business Rule application

await this.userRepository.save(user); // Persist state
}
}
```

### ✅ Best Practice: Primary Adapter (`src/adapters/primary/http/UserController.ts`)

```typescript
// HTTP Controller invoking the Use Case
import { Request, Response } from 'express';
import { DeactivateUserUseCase } from '../../../core/ports/in/DeactivateUserUseCase';

export class UserController {
constructor(private readonly deactivateUserUseCase: DeactivateUserUseCase) {}

async deactivate(req: Request, res: Response) {
try {
await this.deactivateUserUseCase.execute(req.params.id);
res.status(200).send({ message: 'User deactivated successfully' });
} catch (error) {
res.status(400).send({ error: error.message });
}
}
}
```
41 changes: 41 additions & 0 deletions architectures/hexagonal-architecture/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: Hexagonal Architecture (Ports & Adapters) production-ready guidelines and modular breakdown for modern systems.
technology: Hexagonal Architecture
domain: Architecture
level: Senior/Architect
version: Agnostic
tags: [best-practices, clean-code, hexagonal-architecture, ports-and-adapters, system-design, vibe-coding]
ai_role: Senior Software Architect
last_updated: 2026-03-22
---

# 🛑 Hexagonal Architecture Production-Ready Best Practices

# Context & Scope
- **Primary Goal:** Document and execute the best practices for the Hexagonal Architecture pattern.
- **Target Tooling:** AI Agents and Human Developers.
- **Tech Stack Version:** Agnostic

<div align="center">
<img src="https://img.icons8.com/?size=100&id=102832&format=png&color=000000" width="100" alt="Hexagonal Architecture Logo">

**Ports and Adapters for scalable, testable code.**
</div>

---

## 🗺️ Map of Patterns (Hexagonal Modules)

This pattern documentation has been decomposed into specialized modules for zero-approval AI parsing and human comprehension.

- 🌊 **[Data Flow](./data-flow.md):** Understand the execution paths and sequences.
- 📁 **[Folder Structure](./folder-structure.md):** The strict directory blueprints.
- ⚖️ **[Trade-offs](./trade-offs.md):** Pros, cons, and architectural constraints.
- 🛠️ **[Implementation Guide](./implementation-guide.md):** Step-by-step rules and code constraints for Vibe Coding.

## 🚀 The Core Philosophy

Hexagonal Architecture (Ports & Adapters) ensures the core business logic is isolated from specific external technologies.
All interactions with the DB, UI, or other systems happen through **Ports** (interfaces), and are fulfilled by **Adapters** (concrete implementations).

> **AI Constraint:** Always generate the Core Domain first. The Domain must have ZERO dependencies on frameworks or libraries (except language core features).
39 changes: 39 additions & 0 deletions architectures/hexagonal-architecture/trade-offs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
description: Hexagonal Architecture Trade-offs, Pros, Cons, and Architectural constraints for modern software design.
technology: Hexagonal Architecture
domain: Architecture
level: Senior/Architect
version: Agnostic
tags: [best-practices, trade-offs, hexagonal-architecture, ports-and-adapters]
ai_role: Senior Software Architect
last_updated: 2026-03-22
---

# ⚖️ Hexagonal Architecture Trade-offs and Constraints

<div align="center">
**Evaluating the return on investment when choosing Ports and Adapters.**
</div>

---

## 📊 Pros & Cons Matrix

| Feature | ✅ Pros | ❌ Cons |
| :--- | :--- | :--- |
| **Testability** | Extreme isolate testing is native. Domain can run without a DB or UI. | Requires writing numerous Mock objects and Test Doubles. |
| **Flexibility** | Swap out a DB (e.g., PostgreSQL for MongoDB) or Framework instantly. | Boilerplate heavy. Setup time for simple CRUD apps is unjustified. |
| **Domain Focus** | Keeps the team focused strictly on business value logic. | Steep learning curve for Junior developers used to tight MVC coupling. |
| **Delayed Decisions** | You don't need to pick a Database or UI Framework on day 1. | Over-engineering risk for startups seeking rapid MVP validation. |

## ⛔ Hard Rules & Architectural Constraints

1. **Dependency Inversion Enforcement:** The Core Domain must define its dependencies via Interfaces (Ports). It does not "call" Adapters. Adapters implement the Ports, and the application wiring (Dependency Injection container) provides the instances at runtime.
2. **Framework Agnosticism in Core:** No ORM decorators (like `@Entity` or `@Column`) or Web Framework decorators (like `@Get` or `@Req`) are allowed inside `src/core/domain`.
3. **Data Mapping Requirement:** Adapters must translate specific Data Objects (e.g., HTTP requests, DB rows) into clean Domain Entities before passing them inward. When returning data, Domain Entities must be translated back into Adapter-specific DTOs before reaching the outside world.
4. **Ports Exclusivity:** A Port belongs to the Core Domain. The Core dictates what the Port looks like based on its business needs, not based on what a specific Adapter provides. An external library API should never dictate a Port's signature.

## 🏆 When to use Hexagonal Architecture

- **Use when:** The project is expected to live for 5+ years, undergoes frequent changes in external vendor tools, requires high test coverage, and involves complex business rules.
- **Do not use when:** You are building a quick prototype, a purely data-driven CRUD application where the DB schema *is* the domain model, or if the engineering team is highly inexperienced with SOLID principles.
Loading
Loading