Overview
The jMolecules annotations help enforce Hexagonal Architecture principles, ensuring modularity, separation of concerns, and technology independence. This document explains the constraints (must depend on
and must not depend on
), provides examples, and outlines how these concepts are applied in a real-world project.
Dependency Rules: Must Depend On and Must Not Depend On
In Hexagonal Architecture, dependency rules ensure a clear boundary between your application’s core logic and its infrastructure or technology-specific details.
1. Must Depend On
- Definition: Specifies which components or interfaces a given component is allowed to rely on or interact with.
- Purpose: Ensures proper communication between components and avoids direct access to unrelated parts of the system.
- Example: A
PrimaryAdapter
must depend on aPrimaryPort
to ensure it communicates with the core application only through defined interfaces.
2. Must Not Depend On
- Definition: Specifies which components a given component is not allowed to rely on or interact with.
- Purpose: Prevents tight coupling and enforces architectural boundaries, keeping the system modular and technology-independent.
- Example: The application core (
@Application
) must not depend on@Adapter
to ensure the core logic remains decoupled from technology.
jMolecules Annotations and Their Constraints
Here’s a table summarizing the allowed and restricted dependencies for each jMolecules annotation:
Annotation | Must Depend On | Must Not Depend On | Represents |
---|---|---|---|
@Adapter | @Port | @Application | Technology-specific implementation driving or implementing a Port . |
@Application | @Port | @Adapter | Core business logic, independent of technology or framework. |
@Port | None | Technology-specific code | A contract between the application and the outside world. |
@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. |
@SecondaryAdapter | @SecondaryPort | @PrimaryPort , @Adapter | Technology-specific implementation for external systems driven by Port . |
@SecondaryPort | None | Technology-specific code | Core interface for application-driven communication with external systems. |
Example: User Registration System
Let’s build a user registration system to see these annotations and rules in action.
Core Interfaces and Adapters
-
PrimaryPort:
UserService
- Exposes methods like
registerUser(user: User)
for external systems to drive the application.
- Exposes methods like
-
PrimaryAdapter:
HttpController
- Implements a REST API, delegating user registration tasks to
UserService
.
- Implements a REST API, delegating user registration tasks to
-
SecondaryPort:
UserRepository
- Abstracts persistence operations, such as saving users to a database.
-
SecondaryAdapter:
JpaUserRepository
- Implements
UserRepository
using JPA to interact with the database.
- Implements
Code Example
PrimaryPort (UserService)
PrimaryAdapter (HttpController)
SecondaryPort (UserRepository)
SecondaryAdapter (JpaUserRepository)
Application Core
Dependency Rules in Action
-
HttpController
(PrimaryAdapter):- Must Depend On:
UserService
(PrimaryPort). - Must Not Depend On:
UserRepository
(SecondaryPort) orJpaUserRepository
(SecondaryAdapter).
- Must Depend On:
-
UserServiceImpl
(Application):- Must Depend On:
UserRepository
(SecondaryPort). - Must Not Depend On:
JpaUserRepository
(SecondaryAdapter).
- Must Depend On:
-
JpaUserRepository
(SecondaryAdapter):- Must Depend On:
UserRepository
(SecondaryPort). - Must Not Depend On:
UserService
(PrimaryPort) orHttpController
(PrimaryAdapter).
- Must Depend On:
Why These Rules Matter
- Encapsulation:
- Business logic (
@Application
) is independent of infrastructure (@Adapter
).
- Business logic (
- Testability:
- Core components depend on abstract
Port
s, making them easy to mock.
- Core components depend on abstract
- Technology Independence:
- Adapters can be swapped (e.g., JPA to MongoDB) without changing the core logic.
Conclusion
jMolecules annotations enforce Hexagonal Architecture principles by clearly defining constraints on component interactions. By adhering to these rules:
- Your system becomes more modular, testable, and maintainable.
- The business logic remains independent of technology-specific details.