HoCAT (Hands-on Clean Architecture Template) is a template project based on the principles of Clean Architecture. It provides a production-ready, battle-tested, and evolvable architectural foundation for Java applications, designed to help developers build high-quality, maintainable software systems for the long term.
The core value of HoCAT lies in its clear, decoupled architecture. It is not just a theoretical showcase but a set of engineering practices ready for production use.
For a detailed guide on the architecture design, please refer to the Clean Architecture Implementation Guide (in Chinese).
For a deeper dive into the engineering decisions, implementation details, and the reasoning behind our technology and tooling choices (such as Gradle conventions and testing strategies), please refer to the HoCAT project's internal documentation: .hocat/README.md.
Theories like Clean Architecture are highly abstract. HoCAT provides an executable, buildable engineering blueprint that translates them into practice. Through its Gradle multi-module structure, it physically enforces architectural dependency rules and answers concrete engineering questions like "how to organize code" and "how to guarantee dependency direction", making abstract theory concrete and actionable.
HoCAT protects the core business logic by enforcing a unidirectional dependency rule, ensuring each component depends only on the bare minimum it needs:
domain
: Defines pure business rules, independent of any external frameworks, making it the most stable part of the system.application
: Orchestrates business flows, defines Use Cases and Ports, and is also independent of specific technologies.adapter
: Implements the interaction logic with external technologies. This is the only place where specific frameworks and tools like Spring Web MVC or Spring Data JPA are introduced.configuration
: Assembles the application. It is responsible for introducing the Spring IoC container and wiring the concreteadapter
implementations into theapplication
's ports via dependency injection.
This design provides significant flexibility in technology choices. Since adapters are pluggable, you can easily swap implementations. For example, the project provides both adapter/persistence
(JPA) and adapter/persistence-jdbc
(Spring Data JDBC). You can switch between them with no changes to the business code. Likewise, adapter/web
and adapter/web-openapi
demonstrate that API implementation strategies are also swappable.
HoCAT adopts a balanced and pragmatic strategy for data transfer between layers, avoiding two common anti-patterns:
- Avoiding a single model that spans all layers: Exposing a database entity directly to the API can leak internal details and create security risks.
- Avoiding a DTO explosion: Creating a separate model for every interaction leads to a proliferation of boilerplate classes and mapping code.
HoCAT's strategy is that each layer owns the data structures for its boundaries. For instance, the web
adapter has its own Request/Response models, while the persistence
adapter has its persistence entities. This approach maintains clear boundaries without sacrificing developer productivity.
A well-designed adapter should also follow the separation of concerns principle internally. HoCAT isolates third-party library dependencies to the smallest possible scope within an adapter. For example, in the persistence adapter, only a few components, such as entity classes annotated with @Entity
(e.g., OrderEntity
) and repositories that inherit from Spring Data interfaces (e.g., OrderJpaRepository
), are directly coupled with the specific data access technology (like Spring Data JPA). The rest of the adapter remains agnostic to the specific persistence technology, achieving a deeper level of isolation.
To understand how the architecture is implemented, here is a tour of the key modules:
domain
: Defines the core business entities and rules; the most stable and pure part of the application.application
: Orchestrates business logic by defining Use Cases and the interfaces (Ports) that the outer layers must implement.adapter
: Connects the application to the outside world by providing concrete implementations for the ports defined in theapplication
layer.configuration
: The final assembly module. It wires everything together using dependency injection.
- Install Java 21
- Install Docker and Docker Compose
Use ./gradlew build
to build and test the entire project.
- Use
./gradlew bootRun
to run the application locally. - To start the database, run
docker compose up -d
in theconfiguration
directory. You can check the dynamically assigned port withdocker compose ps
. - To start local third-party services for contract testing, run
scripts/run-stub-runner-server configuration/src/test/resources/contracts/client 16581
.
Use ./gradlew bootBuildImage
to build a Docker image.
- Formatter: Install the Spotless IDE plugin to auto-format code.
- Foundation: Java 21, Spring Boot 3.5, Spring Bean Validation, Lombok
- Build: Gradle, JaCoCo, Spotless
- Optional Adapters
- Web Adapter: Spring Web MVC, Spring Security, Spring Cloud Contract
- OpenAPI-based Web Adapter: OpenAPI Generator, Spring Web MVC, Spring Security
- Persistence (JPA): Spring Data JPA, MySQL 8, Flyway, Spring Boot Docker Compose Support
- Persistence (JDBC): Spring Data JDBC, MySQL 8, Flyway, Spring Boot Docker Compose Support
- Client Adapter: Spring RestClient, Spring Cloud Contract Stub Runner
- Testing: JUnit 5, AssertJ, Mockito
- Documentation: Markdown, PlantUML
- HoCATLing: A simplified, single-module version of HoCAT, suitable for smaller projects.