Hexagonal Architecture (Ports and Adapters)
Hexagonal Architecture, also known as Ports and Adapters, is the internal structure pattern used within each Tuturuuu microservice to achieve technology independence, testability, and maintainability.Core Concept
The hexagon represents your application’s core business logic. Ports are interfaces that define how the outside world can interact with your application, and Adapters are implementations that connect external technologies to these ports.Layer Structure in Tuturuuu
1. Domain Layer (Core Business Logic)
Location:apps/*/src/domain/
Responsibilities:
- Pure business logic and rules
- Domain models (entities, value objects)
- Domain services (complex business operations)
- Domain events
- No dependencies on external libraries
- No knowledge of databases, APIs, or frameworks
- Fully unit-testable without infrastructure
2. Application Layer (Use Cases / Orchestration)
Location:apps/*/src/application/
Responsibilities:
- Orchestrate domain objects
- Implement use cases (business workflows)
- Transaction management
- Call infrastructure services via ports
3. Infrastructure Layer (Adapters)
Location:apps/*/src/infrastructure/
Responsibilities:
- Implement ports defined by application/domain
- Handle technology-specific details
- Database access, API calls, event publishing
4. Presentation Layer (API / UI)
Location:apps/*/src/app/api/ or apps/*/src/app/
Responsibilities:
- HTTP request/response handling
- Input validation (DTOs)
- Authentication/authorization
- Call application use cases
Key Benefits
1. Technology Independence
The core business logic has zero knowledge of the technologies used. We can swap Supabase for Prisma, Drizzle, or even MongoDB without touching business logic. Example migration:2. Testability
Domain logic can be tested without any infrastructure:3. Maintainability
Clear boundaries make it obvious where code belongs:- Business rules → Domain layer
- Workflows → Application layer
- Technology details → Infrastructure layer
- HTTP handling → Presentation layer
Ports (Interfaces)
Ports are contracts defined by the application/domain:Adapters (Implementations)
Adapters implement the ports:Dependency Injection
Wire adapters to use cases:Directory Structure Example
C4 Component Diagram Explanations
The C4 model provides a structured way to visualize software architecture at different levels of abstraction. At the Component level, we focus on the major structural building blocks and their interactions within a system.Backend – Hexagonal Architecture (C4 Component Diagram)
The Payment Microservice adopts a Hexagonal Architecture to enforce strict separation of concerns, improve testability, and enable flexible integration with external systems. Inbound Flow (Outside → Core): Incoming requests enter the system through Inbound Adapters such as REST controllers and Kafka consumers, which convert incoming Inbound DTOs into internal representations and delegate execution to Application Services. These services coordinate the primary business flows and act as the main entry point to the domain layer. Core Domain Layer: The Domain Layer contains the core business logic through Domain Services and Domain Models, which represent the essential rules and constraints of the payment domain. Domain logic remains completely isolated from external frameworks and is unaware of transport, persistence, or other infrastructure concerns. Outbound Flow (Core → Outside): When external communication or persistence is required, the Application Services call Outbound Ports, whose interfaces define what the domain requires from the outside world. Corresponding Outbound Adapters implement these ports by integrating with external dependencies such as the Payment Database, Redis caches, Kafka event producers, and the Stripe API. Infrastructure and Cross-Cutting Concerns: Configuration components such as Kafka and Redis configs are placed in the Infrastructure Layer, while cross-cutting concerns such as filters and security packages operate at the service edge. Dependency Flow: This structure ensures a one-way dependency flow—from adapters toward the centre—so the domain remains stable, framework-independent, and highly testable. Commands, results, and mappers exist in the implementation but are intentionally omitted at the component level to keep the C4 diagram high-level and focused on architectural boundaries rather than internal Java classes.Frontend – Headless UI React Architecture (C4 Component Diagram)
The frontend is structured using a Headless UI + React Architecture, separating rendering logic from state and behavioural logic to maintain scalability and reusability. Presentation Layer: Each Page acts as a top-level container that orchestrates data loading and renders multiple UI components. Visual components such as ProductTable, ProductCard, and the Search & Filter Panel remain purely presentational, while all stateful or behavioural logic is delegated to dedicated Hooks. These hooks encapsulate actions such as fetching data, handling events, or executing mutations, thereby keeping the UI simple and predictable. Communication Layer: All external communication flows through Service components, which call typed HTTP client utilities responsible for interacting with backend API endpoints. Shared logic such as constant management and fetcher utilities is abstracted into reusable modules, ensuring consistency across the application. Shared Component Library: Reusable UI building blocks are grouped into a shared component library, supporting composability and reducing duplication. Layer Separation: This architecture clearly separates Presentation → Logic → Communication layers, enabling maintainable state management, easier testability, and seamless integration with backend microservices. Since the diagram operates at C4 Component Level, it intentionally excludes internal React implementation details (such as local state, props, or context) and instead focuses on component roles, interactions, and the flow of data through the system. Key Principles:- Separation of Concerns: UI components are purely presentational; hooks handle state and behavior
- Reusability: Shared components and utilities reduce code duplication
- Testability: Clear boundaries enable isolated testing of presentation, logic, and communication layers
- Type Safety: Typed HTTP clients ensure contract adherence between frontend and backend
Related Documentation
- Architectural Decisions - Why we chose hexagonal architecture
- Encapsulation Patterns - Layer boundaries
- Data Fetching - Integration with React