HexaStock is a financial portfolio management system built with Spring Boot, Domain-Driven Design (DDD), and Hexagonal Architecture. This educational project demonstrates best practices for building a robust, maintainable financial application using modern software architecture patterns.
HexaStock 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
What makes this project particularly interesting is its implementation of DDD tactical patterns and Hexagonal Architecture, making it an excellent educational resource for developers looking to understand these architectural approaches in a real-world context.
HexaStock implements Hexagonal Architecture (also known as Ports and Adapters), which separates the application into distinct layers:
The core of the application contains business entities and logic with no dependencies on external frameworks:
- Entities: Portfolio, Holding, Lot, Transaction
- Value Objects: Money, Ticker
- Domain Services: Business rules for buying, selling, and portfolio management
Defines use cases and orchestrates the domain objects:
- Ports: Interfaces that define how the application interacts with the outside world
- Input Ports: Use cases the application provides (e.g.,
PortfolioManagementUseCase) - Output Ports: Services the application needs (e.g.,
PortfolioPort,StockPriceProviderPort)
- Input Ports: Use cases the application provides (e.g.,
- Services: Implementations of the input ports that coordinate domain objects
Connects the application to external systems:
- Inbound Adapters: REST controllers that expose the application's functionality
- Outbound Adapters: JPA repositories, external API clients for stock prices
This architecture provides several benefits:
- Testability: The domain and application logic can be tested in isolation
- Flexibility: Adapters can be replaced without changing the core logic
- Focus on Business Rules: Domain logic is protected from infrastructure concerns
The central aggregate root in the system. A portfolio:
- Belongs to a user/owner
- Maintains a cash balance
- Contains a collection of holdings (stocks)
- Enforces business rules like preventing purchases with insufficient funds
Represents ownership of a specific stock:
- Identified by a ticker symbol (e.g., AAPL for Apple)
- Contains a collection of lots (individual purchases)
- 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
- Used for calculating cost basis and profit/loss when selling
Records all financial activities within a portfolio:
- Types include: BUY, SELL, DEPOSIT, WITHDRAWAL
- Stores details like price, quantity, and timestamp
- For sales, includes profit/loss calculations
- JDK 21 or higher
- Maven
- MySQL (or Docker for running the database)
# Start MySQL using Docker
docker-compose up -d# Clone the repository
git clone https://github.com/yourusername/hexastock.git
cd hexastock
# Build the project
./mvnw clean install
# Run the application
./mvnw spring-boot:runThe application will be available at http://localhost:8081
Access the Swagger UI to explore and test the API:
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 |
Financial applications require careful handling of concurrent operations to maintain data integrity. HexaStock addresses this in several ways:
When retrieving a portfolio, the system uses pessimistic locking to prevent concurrent modifications:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM PortfolioJpaEntity p WHERE p.id = :id")
Optional<PortfolioJpaEntity> findByIdForUpdate(@Param("id") String id);This ensures that when multiple requests attempt to operate on the same portfolio (e.g., buying/selling stocks), one operation completes fully before the next begins.
All services that modify portfolio data are annotated with @Transactional, ensuring that operations either complete fully or roll back completely, maintaining database consistency:
@Service
@Transactional
public class PortfolioStockOperationsService implements PortfolioStockOperationsUseCase {
// ...
}The Portfolio acts as an aggregate root, ensuring that all changes to its contained entities (Holdings, Lots) are consistent. This DDD pattern helps maintain invariants across related entities.
HexaStock uses a comprehensive testing approach:
- Domain entities are thoroughly tested in isolation
- Tests verify business rules and invariants without any infrastructure dependencies
- Each domain concept has dedicated test classes (e.g.,
PortfolioTest,HoldingTest,LotTest)
- Test the interaction between the application and its adapters
- Verify that persistence mechanisms work correctly
- Ensure API endpoints function as expected
To run the tests:
./mvnw testThe Portfolio-Holding-Lot hierarchy demonstrates proper aggregate design in DDD. The Portfolio is the aggregate root, ensuring all changes to Holdings and Lots maintain business invariants. This design prevents inconsistent states, such as selling more shares than owned or having negative balances.
Money and Ticker are implemented as immutable value objects, focusing on what they are rather than who they are. This enhances code clarity and prevents bugs related to primitive obsession.
The system implements First-In-First-Out (FIFO) accounting for stock sales, a common approach in finance. When selling shares, the oldest lots are sold first, which is clearly demonstrated in the Holding entity's sell method.
While not currently implemented, a natural evolution would be to add domain events for significant business operations (e.g., StockSold, CashDeposited). This would allow for features like notifications, audit logging, or event sourcing.
HexaStock could be enhanced with:
- Event-Driven Architecture: Implement domain events to enable more decoupled, reactive features
- User Authentication: Add multi-user support with proper authentication and authorization
- Performance Optimizations: Caching for stock prices and portfolio data
- Extended Financial Features:
- Dividend tracking
- Portfolio analytics and performance metrics
- Tax lot optimization strategies (beyond FIFO)
- Real-Time Updates: WebSocket integration for live price updates
- Reporting: Generate financial reports and tax documents
This project demonstrates the power of Domain-Driven Design and Hexagonal Architecture in creating maintainable, testable applications for complex domains like finance. By separating concerns and focusing on the business core, HexaStock provides a solid foundation that can evolve with changing requirements.
JaCoCo