Live URL here:
https://hexatask.tonys-dev.com/
HexaTask is a full-stack project and task management system built with Domain-Driven Design and Hexagonal Architecture. It features robust authentication, ownership-based authorization, audit logging, and comprehensive observability. Designed for clean code, scalability, and developer experience, HexaTask enables seamless management of projects and tasks with modern UI and API.
graph TD
A[User] -->|Interacts| B[Angular Frontend]
B -->|API Calls| C[Spring Boot Backend]
C -->|DB Access| D[(PostgreSQL Database)]
C -->|Metrics| E[Prometheus]
E --> F[Grafana]
B -->|Static Files| G[Nginx]
G -->|Proxy| C
C -->|Swagger| H[Swagger UI]
subgraph DevOps
I[Docker Compose]
I --> B
I --> C
I --> D
I --> E
I --> F
I --> G
end
Frontend
- Angular 17 (Signals API, Material UI)
- TypeScript 5.4
- RxJS 7.8
- Tailwind CSS 3.4
- Nginx (for static serving & API proxy)
Backend
- Java 17
- Spring Boot 3.4.1
- Spring Security (JWT dual token)
- Hibernate Envers (audit logging)
- Spring Data JPA
- Flyway (DB migrations)
- Micrometer (metrics)
Database & Infrastructure
- PostgreSQL 15
- Docker & Docker Compose
- Prometheus (metrics)
- Grafana (visualization)
Testing & Quality
- JUnit 5, Mockito (backend)
- Jasmine, Karma (frontend)
- Swagger/OpenAPI (API docs)
DevOps & Deployment
- Gradle (backend build)
- Node.js & npm (frontend build)
- Docker Compose (multi-service orchestration)
hexatask/
βββ backend/
β βββ src/
β β βββ main/
β β β βββ java/com/hexatask/hexatask/ # domain, application, infrastructure
β β β βββ resources/ # application.yml, db/migration
β βββ src/test/ # unit & integration tests
β βββ Dockerfile
β βββ build.gradle
βββ frontend/
β βββ src/
β β βββ app/ # core, features, shared
β β βββ main.ts
β βββ package.json
β βββ angular.json
β βββ Dockerfile
βββ scripts/
β βββ setup.sh
β βββ run.sh
β βββ dev/
β βββ start.sh
β βββ stop.sh
β βββ logs.sh
βββ docker-compose.yml
βββ .env (example created by scripts/setup.sh)
βββ README.md
- Java 17+
- Node.js >= 16
- npm (or yarn)
- Docker & Docker Compose
- PostgreSQL 15 (if running backend outside Docker)
-
Clone the repository
git clone https://github.com/TonyS-dev/hexatask.git cd hexatask -
Setup environment
chmod +x scripts/setup.sh scripts/run.sh ./scripts/setup.sh # Edit .env with your values nano .env -
Run the application
./scripts/run.sh
- Or use Docker Compose directly:
docker compose up --build
- Or use Docker Compose directly:
Create a .env file in the root directory with:
DB_NAME=hexatask
DB_USER=youruser
DB_PASSWORD=yourpassword
DB_PORT=5432
JWT_SECRET=your_jwt_secret
JWT_EXPIRATION=900
VITE_API_BASE_URL=http://localhost:8080/api
- Domain-Driven Design (Hexagonal Architecture)
- JWT Dual Token Authentication (Access + Refresh)
- Ownership-based Authorization
- Soft Delete with Audit Trail
- Comprehensive Unit Tests
- Swagger/OpenAPI Documentation
- Docker & Docker Compose Support
- Paginated project listing with filtering
- Real-time metrics (Prometheus/Grafana)
- Responsive Angular UI
- Login/Register: Access the frontend at http://localhost:4200
- Manage Projects/Tasks: Create, update, delete, and filter projects/tasks
- API: Use Swagger UI at http://localhost:8080/swagger-ui.html for API exploration
Example: Running locally
cd backend
./gradlew bootRun
# In another terminal
cd frontend
npm install
npm start- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON:
/v3/api-docs - See backend
HELP.mdfor Spring Boot API guides
Backend:
cd backend
./gradlew testFrontend:
cd frontend
npm test- Production build (frontend):
cd frontend npm run build - Build backend JAR:
cd backend ./gradlew build - Deploy with Docker Compose:
docker compose up --build
- Fork the repository
- Create a feature branch
- Submit a pull request with clear description
MIT License (see LICENSE file or opensource.org/licenses/MIT)
Antonio Santiago (TonyS-dev)
Let me know if you want to add screenshots, more API details, or further customization.
- Java 17+ (Gradle wrapper will download matching distribution)
- Node.js >= 16 (for Angular frontend)
- npm (or yarn) for package management
- Docker & Docker Compose (for containerized setup)
- PostgreSQL 15 (if running backend without Docker)
chmod +x scripts/setup.sh scripts/run.sh
./scripts/setup.sh # First time only - sets up Docker and environment
./scripts/run.sh # Starts all servicesSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # First time only
.\scripts\setup.ps1 # Sets up Docker and environment
.\scripts\run.ps1 # Starts all servicesServices will start at:
- Frontend: http://localhost:4200
- Backend API: http://localhost:8080
- Swagger UI: http://localhost:8080/swagger-ui.html
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000
See scripts/README.md for detailed documentation.
docker compose up --buildAccess the application:
- Frontend: http://localhost:4200
- Backend API: http://localhost:8080
- Swagger UI: http://localhost:8080/swagger-ui.html
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000
Start backend:
cd backend
./gradlew bootRunStart frontend (in another terminal):
cd frontend
npm install
npm startFrontend will be available at http://localhost:4200
User 1 (Owner of sample projects):
- Email: juan@mail.com
- Password: password123
User 2 (Different user):
- Email: anto@mail.com
- Password: password123
Backend (inside backend/):
./gradlew bootRun # Run the Spring Boot app
./gradlew build # Build jar
./gradlew test # Run unit tests
./gradlew clean build -x test # Build without testsFrontend (inside frontend/):
npm install # Install dependencies
npm start # Start Angular dev server
npm run build # Build production bundle
npm run lint # Run ESLintDocker Compose (repo root):
docker compose up --build # Start all services
docker compose down # Stop all services
docker compose logs -f # View logsThe backend exposes /api/auth/login and /api/auth/register endpoints with a secure dual-token approach:
- Access Token (JWT, 15 minutes) β Used for API requests
- Refresh Token (JWT, 30 minutes) β Persisted in DB, enables token rotation
- On token refresh, old tokens are immediately deleted for enhanced security
- Frontend stores both tokens in
localStorageand sends access token inAuthorization: Bearerheader
Every write operation validates that the authenticated user owns the resource:
// Example: Only project owner can activate
if (!project.getOwnerId().equals(currentUserId)) {
throw new UnauthorizedException("403 Forbidden");
}Applied to all critical operations:
- β GetProject β owner verification
- β ActivateProject β owner verification + business rules
- β CreateTask β project owner verification
- β UpdateTaskStatus β project owner verification
- β ListProjects β filtered by owner (no data exposure)
Comprehensive API documentation using Swagger/OpenAPI 3.0.
Access Swagger UI:
π http://localhost:8080/swagger-ui.html
API Groups:
-
Authentication API (
/api/auth)- User registration
- User login (returns dual tokens)
- Token refresh
-
Projects API (
/api/projects)- List user's projects (paginated & filtered)
- Create new project
- Get project details
- Activate project
-
Tasks API (
/api/projects/{projectId}/tasks)- Create task
- Complete task
- List tasks
Authentication in Swagger UI:
- Login via
/api/auth/login - Copy the JWT token from the response
- Click "Authorize" button at the top
- Enter:
Bearer {your-token} - Click "Authorize" and close
Alternative API Docs:
- OpenAPI JSON: http://localhost:8080/v3/api-docs
- OpenAPI YAML: http://localhost:8080/v3/api-docs.yaml
Actuator Endpoints:
curl http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/metrics
curl http://localhost:8080/actuator/prometheusPrometheus & Grafana:
- Prometheus UI: http://localhost:9090
- Grafana: http://localhost:3000
Metrics available:
- HTTP server metrics:
http.server.requests - JVM metrics:
jvm.memory.*,jvm.gc.*,process.cpu.* - Data source metrics (HikariCP):
hikaricp.* - Cache metrics:
cache.*
In production, lock down actuator endpoints with proper authentication and only expose minimum required endpoints.
Strict separation of concerns across 4 layers:
domain/ β Pure business logic (no Spring, no JPA)
ββ model/ β Entities (User, Project, Task)
ββ ports/ β Interfaces (UseCases IN, Dependencies OUT)
ββ events/ β Domain events (ProjectActivated, TaskCompleted)
application/ β Business logic orchestration
ββ usecase/ β Implementation of ports
ββ dto/ β Data Transfer Objects
infrastructure/ β Framework & external integration
ββ web/ β Controllers, REST endpoints
ββ adapters/ β Repository implementations, Security, Audit
ββ entities/ β JPA entities (separate from domain)
ββ mappers/ β Domain β Entity conversions
ββ config/ β Spring configuration
presentation/ β Frontend (Angular)
- Ports & Adapters: Domain is independent of frameworks
- Dependency Injection: All dependencies injected via constructors
- Strategy Pattern: Configurable notification system
- Mapper Pattern: Strict separation between Domain and JPA entities
- Soft Delete: Logical deletion with
deletedflag - Event Publishing: Domain events for decoupled cross-cutting concerns
- JWT validated on every request via
JwtAuthenticationFilter - Credentials stored securely with BCrypt password encoder
- Refresh tokens persisted in DB for revocation capability
Flyway manages all schema changes automatically:
V1__create_tables.sqlβ Initial schema (User, Project, Task)V2__create_audit_tables.sqlβ Audit logging tablesV3__add_task_status.sqlβ Add task status columnV5__insert_test_data.sqlβ Test credentials and sample data
Features:
- Soft delete pattern (records marked as deleted, never removed)
- Optimistic locking via
versionfield - Foreign keys with CASCADE DELETE (logical)
- Indexes on frequently queried columns (owner_id, created_at)
Test credentials and sample projects are inserted via Flyway migrations. Data is idempotent via ON CONFLICT clauses.
Unit Tests (JUnit 5 + Mockito):
cd backend
./gradlew testTest Coverage (5 critical tests):
- β
ActivateProject_WithTasks_ShouldSucceedβ Happy path - β
ActivateProject_WithoutTasks_ShouldFailβ Business rule validation - β
ActivateProject_ByNonOwner_ShouldFailβ Ownership validation - β
CompleteTask_AlreadyCompleted_ShouldFailβ State validation - β
CompleteTask_TaskNotFound_ShouldFailβ Resource not found
All mocks injected, no Spring context loaded (true unit tests).
Why: Users should only access their own resources.
How: CurrentUserPort extracts authenticated user ID from JWT, then each use case validates:
UUID currentUserId = currentUserPort.getCurrentUserId();
if (!resource.getOwnerId().equals(currentUserId)) {
throw new UnauthorizedException("403 Forbidden");
}Result: 403 Forbidden if user tries to access/modify another user's resource.
Why: Access tokens are short-lived to minimize risk if stolen, refresh tokens enable persistent sessions.
How:
- Access Token: 15 minutes (in-memory JWT)
- Refresh Token: 30 minutes (persisted in DB)
- On refresh: old token deleted, new pair generated (rotation)
Result: Better security than single long-lived token, better UX than forcing re-login every 15 min.
Why: Clean separation enables easy testing and refactoring.
How:
- Domain layer has NO Spring dependencies
- Application layer contains use cases
- Infrastructure adapts to frameworks (Spring, JPA, REST)
- Tests mock all output ports
Result: Business logic isolated from framework changes, highly testable.
Why: Audit trail and compliance require keeping deleted data.
How: Entities have deleted: boolean flag, queries filter WHERE deleted = false automatically.
Result: No permanent data loss, full audit history maintained.
Why: Keep domain entities independent of JPA annotations.
How:
- Domain entities (User, Project, Task) are POJOs
- JPA entities (UserEntity, ProjectEntity, TaskEntity) have
@Entityannotations - Mappers convert between layers
Result: Domain code has zero framework knowledge, easy to port to different ORM.
Why: Audit and notifications shouldn't be tightly coupled to business logic.
How:
- Use cases publish domain events (ProjectActivatedEvent, TaskCompletedEvent)
- Infrastructure listeners subscribe to events
- Listeners are implementations of output ports
Result: Use cases focused on business logic, cross-cutting concerns handled by adapters.
hexatask/
βββ backend/
β βββ src/main/java/com/hexatask/hexatask/
β β βββ domain/ # Pure business logic (no Spring)
β β β βββ model/ # Domain entities
β β β βββ ports/ # Interfaces (in/out)
β β β βββ events/ # Domain events
β β βββ application/ # Use cases & orchestration
β β βββ infrastructure/ # Adapters & frameworks
β β β βββ web/ # Controllers
β β β βββ adapters/ # Repository, Security, Audit
β β β βββ entities/ # JPA entities
β β β βββ mappers/ # Domain β Entity mapping
β β β βββ config/ # Spring config
β β βββ HexataskApplication.java
β βββ src/test/java/ # Unit tests (JUnit 5 + Mockito)
β βββ src/main/resources/
β β βββ application.yml # Configuration
β β βββ db/migration/ # Flyway migrations
β βββ Dockerfile
β βββ build.gradle
β βββ gradlew
βββ frontend/
β βββ src/
β β βββ app/
β β β βββ core/ # Services, guards, interceptors
β β β βββ features/ # Feature modules (auth, projects, tasks)
β β β βββ shared/ # Shared components
β β βββ main.ts
β βββ Dockerfile
β βββ angular.json
β βββ package.json
β βββ tsconfig.json
βββ docker-compose.yml
βββ scripts/ # Dev helper scripts
βββ README.md # <- You are here
- Axios is configured centrally with an interceptor to add
Authorization: Bearer <token>to outgoing requests - Server-state is handled via RxJS observables and Angular's reactive patterns
- Core services manage API calls, authentication, and route guards
- Feature modules encapsulate domain-specific logic (auth, projects, tasks)
- Angular Material provides UI components with TailwindCSS customization
Frontend not loading?
rm -rf node_modules dist .angular/cache
npm install
npm startBackend compilation error?
./gradlew clean build -x testDatabase migration failed?
# Ensure PostgreSQL is running
docker ps
# Check logs for migration errors
docker compose logs postgresSwagger UI not loading?
- Backend must be running on port 8080
- Try: http://localhost:8080/swagger-ui.html
- Check backend logs:
docker compose logs backend
- Frontend production build:
cd frontend && npm run build - Backend containerized via
backend/Dockerfile - Both deployable with Docker / Kubernetes
- Use provided
docker-compose.ymlfor orchestration
Contributions are welcome. Suggested workflow:
- Fork the repo
- Create a feature branch
- Add tests where appropriate
- Open a PR describing the change
Please follow existing code style and run linting before opening pull requests.
This project is licensed under the MIT License. See the LICENSE file for details.
- Name: Antonio Santiago (TonyS-dev)
- GitHub: https://github.com/TonyS-dev
- Email: santiagor.acarlos@gmail.com
