A full-stack Todo application built with Spring Boot and Vue 3, demonstrating robust software engineering practices including Hexagonal Architecture, BDD/TDD, and dual-track CI/CD pipelines.
The project follows a clean separation of concerns:
frontend/: Vue 3 application (Vite, TypeScript, Pinia).src/main/java/: Spring Boot backend following Hexagonal Architecture.domain/: Pure business logic and domain models (e.g.,Todo,Tag).repository/: Storage interfaces and implementations (memoryvspersistence).service/: Business use cases (TodoService).controller/: REST adapters.config/: Spring configuration classes.
src/test/resources/features/: Cucumber Gherkin feature files (in-memory,database,restful).scripts/: Python automation scripts for E2E testing (auto_test.py).vendor/: Third-party dependencies (e.g.,jh_utils).
Todo/
├── .github/
│ └── workflows/
│ ├── ci.yml # Backend CI: Maven + Cucumber + PostgreSQL
│ └── run_jenkins.yml # Frontend CI: Dockerized Jenkins + Selenium
├── frontend/
│ ├── src/
│ │ ├── api/ # API client (Axios)
│ │ ├── router/ # Vue Router configuration
│ │ ├── stores/ # Pinia state management
│ │ ├── views/ # Vue pages (Dashboard, TodoList)
│ │ ├── App.vue # Root component
│ │ └── main.ts # Entry point
│ └── vite.config.ts # Vite config (Proxy to backend port 8000)
├── scripts/
│ └── auto_test.py # E2E Test Script (Selenium + jh_utils)
├── src/
│ ├── main/
│ │ ├── java/com/todoapp/
│ │ │ ├── config/ # Spring Configuration (Memory/DB profiles)
│ │ │ ├── controller/ # REST Controllers & DTOs
│ │ │ ├── domain/ # Core Domain Models (Hexagonal Core)
│ │ │ ├── repository/ # Repository Interfaces & Implementations
│ │ │ ├── service/ # Business Logic Services
│ │ │ └── TodoAppApplication.java
│ │ └── resources/
│ │ ├── application.properties # Main configuration
│ │ └── schema.sql # Database schema
│ └── test/
│ ├── java/com/todoapp/ # Test Steps & Runner
│ └── resources/features/ # Cucumber Feature Files (Gherkin)
├── vendor/
│ └── jh_utils/ # Third-party utility library (Submodule)
├── Dockerfile # Jenkins agent image definition
├── Jenkinsfile # Jenkins pipeline definition
├── job.xml # Jenkins job configuration template
├── pom.xml # Maven build configuration
└── README.md # Project documentation
- Framework: Vue 3 (Composition API)
- Language: TypeScript
- Build Tool: Vite
- State Management: Pinia
- Styling: Modern CSS
- Configuration: The frontend is currently hardcoded to communicate with the backend on port
8000, matching the default backend configuration.
- Framework: Spring Boot 3
- Language: Java 21
- Persistence: Spring Data JDBC
- In-Memory:
ConcurrentHashMapbased implementation for fast testing. - Database: PostgreSQL for production/persistence.
- In-Memory:
- Architecture: Hexagonal Architecture (Ports and Adapters), ensuring the domain logic remains independent of frameworks and databases.
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│TodoService │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Todo createTodo(String title, String description, java.util.Set<String> tagNames) │
┌─────────────────────────────────┐ │Todo updateTodoByTitle(String oldTitle, String newTitle, String description, java.util.Set<String> tagNames)│
│Todo │ │void deleteByTitle(String title) │
├─────────────────────────────────┤ │void clearAll() │
│Long id │ ┌────────────────────┐ │Todo markCompletedByTitle(String title) │
│String title │ │DashboardService │ │Todo markPendingByTitle(String title) │
│String description │ ├────────────────────┤ │java.util.List<Todo> listTodos() │
│boolean completed │ │int completedCount()│ │java.util.List<Todo> searchTodos(String query) │
│java.time.LocalDateTime createdAt│ │int pendingCount() │ │java.util.List<Todo> listTodosBefore(java.time.LocalDateTime before) │
│java.time.LocalDateTime updatedAt│ └────────────────────┘ │java.util.List<Todo> listTodosBefore(java.time.LocalDateTime before, int limit) │
│java.util.Set<Tag> tags │ │java.util.List<Todo> findByTag(String tag) │
│void markCompleted() │ │Todo addTags(String title, java.util.Set<String> tagNames) │
│void markPending() │ │Todo removeTags(String title, java.util.Set<String> tagNames) │
└─────────────────────────────────┘ │int countCompleted() │
| │int countPending() │
| │Todo getByTitle(String title) │
| └────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
| |
| |
| ┌─────────────────────────────────────────────────────────────┐ |
| │TodoRepository │ |
| ├─────────────────────────────────────────────────────────────┤ ┌──────────────────────────────────────────┐
┌───────────┐ │Todo save(Todo) │ │TagRepository │
│Tag │ │java.util.Optional<Todo> findById(Long) │ ├──────────────────────────────────────────┤
├───────────┤ │java.util.Optional<Todo> findByTitle(String) │ │Tag save(Tag) │
│Long id │ │java.util.List<Todo> findAll() │ │java.util.Optional<Tag> findById(Long) │
│String name│ │void deleteById(Long) │ │java.util.Optional<Tag> findByName(String)│
└───────────┘ │java.util.List<Todo> searchByText(String) │ │java.util.List<Tag> findAll() │
│java.util.List<Todo> findBefore(java.time.LocalDateTime, int)│ │void deleteAll() │
│java.util.List<Todo> findByTag(String) │ └──────────────────────────────────────────┘
│void deleteAll() │
└─────────────────────────────────────────────────────────────┘
View the complete PlantUML diagram (requires a PlantUML viewer).
The project utilizes a dual-track CI/CD strategy to balance speed and reliability:
-
Backend CI (
.github/workflows/ci.yml):- Tool: GitHub Actions.
- Scope: Validates backend business logic and persistence.
- Stack: Maven + Cucumber.
- Profile: Runs with a real PostgreSQL database to ensure data integrity and SQL correctness.
-
Frontend/E2E CI (
.github/workflows/run_jenkins.yml):- Tool: Jenkins (running in Docker).
- Scope: Validates the full application stack (Frontend + Backend) and UI interactions.
- Stack: Jenkins Pipeline (
Jenkinsfile) + Selenium (scripts/auto_test.py). - Profile: Runs the backend in In-Memory mode (
-Dtodo.repository.type=memory) for lightweight, high-speed execution without database overhead.
In addition to automated tests, a comprehensive manual testing campaign was conducted to ensure usability and functional correctness from an end-user perspective.
The following documents provide a complete overview of the manual testing process:
The application uses Spring Profiles to manage different environments:
- Production: Intended to run as a compiled JAR.
- Test Support: The Maven environment fully supports the
testprofile.- Specific interfaces or configurations not meant for production (like the In-Memory repository or test-specific security configs) are strictly guarded by
@Profile("test")to prevent exposure in production environments.
- Specific interfaces or configurations not meant for production (like the In-Memory repository or test-specific security configs) are strictly guarded by
- Properties: Configuration is managed via
application.propertiesand programmaticConfigclasses insrc/main/java/com/todoapp/config/.
This project was developed using Behavior-Driven Development (BDD) and Test-Driven Development (TDD), leveraging Cucumber as the foundation.
- Three Semantic Interfaces:
- In-Memory: For rapid domain logic verification.
- Database: For persistence layer verification.
- Restful: For API contract verification.
- Hexagonal Architecture: The architecture supports swapping these adapters (memory vs database) seamlessly without changing the core domain logic.
- Agenic Coding: The BDD/TDD approach facilitated fast, AI-assisted agentic coding by providing clear executable specifications.
- Refactoring: A distinct "Blue" (Refactor) phase was employed to optimize code structure and quality after getting tests to pass.
This project is licensed under the MIT License - see the LICENSE file for details.