HexaStock is a financial portfolio management system built with Spring Boot, Domain-Driven Design (DDD), and Hexagonal Architecture. This educational project demonstrates practical implementation of these architectural patterns in a real-world domain: financial portfolio management.
HexaStock is designed as a teaching tool for engineering students learning Hexagonal Architecture and Domain-Driven Design. Rather than theoretical examples, it provides a complete, testable system that handles realistic business requirements.
The system allows users to manage investment portfolios, including:
- Creating portfolios and managing cash through deposits and withdrawals
- Buying and selling stocks using real-time market prices
- Tracking holdings with detailed purchase history (lots)
- Viewing transaction history and calculating profit/loss using FIFO accounting
What makes this project particularly valuable for learning is its implementation of DDD tactical patterns (aggregates, value objects, domain services) and Hexagonal Architecture (ports and adapters), making the separation between business logic and infrastructure concerns explicit and testable.
The sell stock use case has been deliberately chosen as the central reference example because it is the richest and most sophisticated one in the project. It concentrates the highest amount of domain logic and therefore illustrates particularly well the power of Domain-Driven Design and Hexagonal Architecture.
This use case demonstrates:
- Aggregate boundary protection (Portfolio as aggregate root)
- FIFO accounting logic implemented in the domain layer
- Transactional consistency across multiple operations
- Domain exception handling and HTTP status code mapping
- Orchestration by application services while aggregates enforce invariants
By understanding this use case thoroughly, students will be able to progress confidently through the rest of the practice and apply the same patterns to other use cases.
For a comprehensive deep dive into the sell stock execution flow, including step-by-step traces, architectural decisions, and hands-on exercises, refer to the dedicated Sell Stock Tutorial.
Before starting, ensure you have the following installed:
- Java Development Kit (JDK) 21 or higher
- Apache Maven 3.6 or higher
- IntelliJ IDEA Ultimate (recommended for full Spring Boot support)
- Docker (for running MySQL in a container)
- Git
You should have basic knowledge of Java, object-oriented programming, and familiarity with Spring Boot concepts.
Clone the repository from GitHub:
git clone https://github.com/alfredorueda/HexaStock.git
cd HexaStock- Launch IntelliJ IDEA
- Select "Open" from the welcome screen
- Navigate to the cloned HexaStock directory
- Select the
pom.xmlfile and choose "Open as Project" - Wait for Maven to download dependencies and index the project
IntelliJ IDEA will automatically detect the Spring Boot configuration and Maven structure.
Start MySQL using Docker:
docker-compose up -dThis will start a MySQL container on port 3307 with the required database configuration.
HexaStock includes comprehensive unit and integration tests that verify both domain logic and infrastructure adapters.
Execute all tests using Maven:
./mvnw clean testThis runs unit tests (classes ending in Test.java) using the Surefire plugin. To run both unit and integration tests:
./mvnw clean verifyThis executes unit tests and integration tests (classes ending in IT.java) using the Failsafe plugin, and generates a merged code coverage report.
To run all tests:
- Open the Project view
- Right-click on
src/test/java - Select "Run 'All Tests'"
To run a specific test class:
- Navigate to the test class (e.g.,
PortfolioTest.java) - Right-click on the class name
- Select "Run 'PortfolioTest'"
HexaStock distinguishes between two categories of tests:
Domain Unit Tests verify business logic in isolation without any infrastructure dependencies. Examples include PortfolioTest, HoldingTest, LotTest, and MoneyTest. These tests run fast and validate that domain invariants are correctly enforced.
Integration Tests verify the interaction between adapters and the application core. Examples include PortfolioRestControllerIntegrationTest. These tests use Testcontainers to spin up a real MySQL database and validate end-to-end flows through the REST API.
CRITICAL: The application will NOT start unless you activate the required Spring profiles. You must specify:
-
Persistence profile (mandatory):
jpa- Activates JPA/Hibernate persistence with MySQL
-
Stock price provider profile (exactly one must be chosen):
finhub- Uses the Finnhub API for stock pricesalphaVantage- Uses the AlphaVantage API for stock prices
Valid profile combinations:
jpa,finhubjpa,alphaVantage
- Locate the main class:
src/main/java/cat/gencat/agaur/hexastock/HexaStockApplication.java - Right-click on the class and select "Run 'HexaStockApplication'"
- The application will fail to start because profiles are not set
- Open "Run > Edit Configurations"
- Select the HexaStockApplication run configuration
- In the "Active profiles" field, enter the profiles as a comma-separated list:
- For Finnhub:
jpa,finhub - For AlphaVantage:
jpa,alphaVantage
- For Finnhub:
- Click "OK" and run the application again
The application will start on port 8081. You should see log messages indicating successful startup.
./mvnw spring-boot:run -Dspring-boot.run.profiles=jpa,finhubor
./mvnw spring-boot:run -Dspring-boot.run.profiles=jpa,alphaVantageEach profile configures a different adapter for the StockPriceProviderPort. The Finnhub adapter calls the Finnhub API, while the AlphaVantage adapter calls the AlphaVantage API. The domain and application layers remain unchanged regardless of which adapter is active. This demonstrates the flexibility of Hexagonal Architecture.
Both stock price providers require API keys. Currently, all API keys are configured directly in the single existing file:
src/main/resources/application.properties
Open this file and locate the following properties:
# Finhub API settings (used when profile is set to 'finhub')
finhub.api.url=https://finnhub.io/api/v1
finhub.api.key=your_finhub_key_here
# Alpha Vantage API configuration (used when profile is set to 'alphaVantage')
alphaVantage.api.base-url=https://www.alphavantage.co/query
alphaVantage.api.key=your_alphavantage_key_hereNote: Profile-specific properties files (such as application-finhub.properties or application-alphavantage.properties) do NOT exist yet. All configuration is currently centralized in the main application.properties file.
You can obtain free-tier API keys from:
- Finnhub: https://finnhub.io/
- AlphaVantage: https://www.alphavantage.co/
Replace the placeholder values with your actual API keys. Alternatively, you can set them as environment variables to keep credentials out of version control.
HexaStock provides pre-configured HTTP requests for testing the REST API manually. These requests are located in:
- Navigate to doc/calls.http
- Open the file in the editor
- Ensure the application is running
- Click the green "Run" arrow next to any request to execute it
Execute the requests in sequence to simulate a complete portfolio lifecycle:
- Create a portfolio
- Deposit cash into the portfolio
- Buy stocks (AAPL, MSFT, etc.)
- Check portfolio status
- Sell stocks
- View transaction history
Each request includes example payloads and expected responses.
Access the Swagger UI to explore and test the API interactively:
http://localhost:8081/swagger-ui.html
| Endpoint | Method | Description | Request Body | Response |
|---|---|---|---|---|
/api/portfolios |
POST | Create a new portfolio | {"ownerName":"John Doe"} |
Portfolio details |
/api/portfolios/{id} |
GET | Get portfolio details | - | Portfolio details |
/api/portfolios/{id}/deposits |
POST | Deposit cash | {"amount":1000.00} |
Status 200 |
/api/portfolios/{id}/withdrawals |
POST | Withdraw cash | {"amount":500.00} |
Status 200 |
/api/portfolios/{id}/purchases |
POST | Buy stock | {"ticker":"AAPL","quantity":10} |
Status 200 |
/api/portfolios/{id}/sales |
POST | Sell stock | {"ticker":"AAPL","quantity":5} |
Sale result with profit/loss |
/api/portfolios/{id}/transactions |
GET | Get transaction history | - | List of transactions |
/api/stocks/{symbol} |
GET | Get current stock price | - | Stock price information |
HexaStock implements Hexagonal Architecture, which organizes code into three primary layers: domain, application, and adapters.
The core of the application contains business entities and logic with no dependencies on external frameworks:
- Entities: Portfolio (aggregate root), Holding, Lot, Transaction
- Value Objects: Money, Price, ShareQuantity, Ticker, PortfolioId, HoldingId, LotId, TransactionId, SellResult, StockPrice
- Domain Exceptions: Business rule violations expressed in domain language
Defines use case interfaces (ports) and implements orchestration logic in application services:
- Inbound Ports: Use cases the application provides (e.g.,
PortfolioStockOperationsUseCase) - Outbound Ports: Services the application needs (e.g.,
PortfolioPort,StockPriceProviderPort) - Application Services: Coordinate domain objects, call ports, manage transactions
Connects the application to the outside world:
- Inbound Adapters: REST controllers that receive HTTP requests and call use case ports
- Outbound Adapters: JPA repositories, HTTP clients for external APIs (Finnhub, AlphaVantage)
This separation enables independent testing of business logic, flexible adapter replacement, and clear architectural boundaries that prevent infrastructure concerns from leaking into business rules.
The central aggregate root in the system. A portfolio maintains a cash balance and contains a collection of holdings. It enforces business rules such as preventing purchases with insufficient funds and ensuring all changes to contained entities maintain consistency.
Represents ownership of a specific stock identified by a ticker symbol (e.g., AAPL for Apple). Contains a collection of lots and manages selling using FIFO (First-In-First-Out) accounting.
Represents a specific purchase of shares. Records the quantity, price, and purchase date. Tracks how many shares remain unsold and is used for calculating cost basis and profit/loss when selling.
Records all financial activities within a portfolio. Types include PURCHASE, SALE, DEPOSIT, and WITHDRAWAL. Stores details like price (Price), quantity (ShareQuantity), and timestamp. For sales, includes profit/loss calculations using Money.
Now that you have the application running and understand the basic structure, explore these resources:
Read the comprehensive tutorial on the sell stock use case.
This document traces the execution path from HTTP request to domain logic to persistence, explaining why application services orchestrate while aggregates protect invariants. It includes hands-on exercises to reinforce your understanding.
Start from the REST controllers in adapter.in, follow calls through application services in application.service, and examine domain logic in the model package.
Review the test classes to understand how domain rules are verified in isolation and how integration tests validate end-to-end behavior.
Explore the documentation in the doc/ directory for API specifications, architectural diagrams, and design decisions.
HexaStock maintains high test coverage to ensure reliability and facilitate refactoring. The project uses JaCoCo for coverage reporting.
This is an educational project. Students are encouraged to experiment with adding new features or modifying existing behavior to gain practical experience with the architectural patterns in action.
