jMolecules Hexagonal Architecture

Mar 06, 2026 · 3 min read

software-architecture hexagonal-architecture ports-and-adapters jmolecules dependency-inversion clean-architecture java kotlin

Contents

Overview

The jMolecules annotations help enforce Hexagonal Architecture (Ports & Adapters) principles, ensuring modularity, separation of concerns, and technology independence.

Hexagonal Architecture — Ports & Adapters with driving and driven sides

In Hexagonal Architecture, the application core communicates with the outside world exclusively through Ports (interfaces). Adapters implement these ports, connecting the core to specific technologies.

Dependency Rules

Dependencies always point inward. Adapters depend on Ports. The Application core depends on nothing external.

Hexagonal Architecture Dependency Matrix

Must Depend On

Specifies which components a given component is allowed to rely on. Ensures proper communication through defined interfaces.

Must Not Depend On

Specifies which components a given component cannot rely on. Prevents tight coupling and enforces architectural boundaries.

jMolecules Annotations and Their Constraints

Annotation Must Depend On Must Not Depend On Represents
@PrimaryAdapter @PrimaryPort @SecondaryPort, @Adapter Technology-specific implementation driving the application via Port
@PrimaryPort None @Adapter, @SecondaryPort Core interface for external systems to interact with the application
@Application @Port @Adapter Core business logic, independent of technology or framework
@SecondaryPort None Technology-specific code Core interface for application-driven communication with external systems
@SecondaryAdapter @SecondaryPort @PrimaryPort, @Adapter Technology-specific implementation for external systems driven by Port

Example: User Registration System

User Registration Flow — POST /users flows through all components

PrimaryPort (UserService)

@PrimaryPort
interface UserService {
    fun registerUser(user: User): String
}

PrimaryAdapter (HttpController)

@PrimaryAdapter
@RestController
class HttpController(private val userService: UserService) {

    @PostMapping("/users")
    fun register(@RequestBody user: User): ResponseEntity<String> {
        val userId = userService.registerUser(user)
        return ResponseEntity.ok(userId)
    }
}

Application Core (UserServiceImpl)

@Application
class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun registerUser(user: User): String {
        // Business logic: Validate, process, and save user
        return userRepository.save(user)
    }
}

SecondaryPort (UserRepository)

@SecondaryPort
interface UserRepository {
    fun save(user: User): String
    fun findById(userId: String): User?
}

SecondaryAdapter (JpaUserRepository)

@SecondaryAdapter
@Repository
class JpaUserRepository(
    private val jpaRepository: CrudRepository<User, String>
) : UserRepository {

    override fun save(user: User): String {
        return jpaRepository.save(user).id
    }

    override fun findById(userId: String): User? {
        return jpaRepository.findById(userId).orElse(null)
    }
}

Dependency Rules in Action

HttpController (@PrimaryAdapter)

UserServiceImpl (@Application)

JpaUserRepository (@SecondaryAdapter)

Why These Rules Matter

Why Hexagonal Architecture Matters

Encapsulation

Business logic (@Application) is fully independent of infrastructure (@Adapter). No framework leaks in.

Testability

Core components depend on abstract Ports, making them easy to mock. No DB or HTTP needed in tests.

Technology Independence

Adapters can be swapped (e.g., JPA to MongoDB, REST to gRPC) without changing the core logic.

Modularity

Clear boundaries between driving and driven sides. Each adapter is isolated and replaceable.

Conclusion

jMolecules annotations enforce Hexagonal Architecture principles by clearly defining constraints on component interactions. By adhering to these rules: