Skip to content

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.

License

Notifications You must be signed in to change notification settings

ISEP-Projects-JH/Todo

Repository files navigation

TodoApp

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.

CI Coverage Jenkins

License: MIT Top Language Languages

Project Structure

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 (memory vs persistence).
    • 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

Tech Stack

Frontend

  • 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.

Backend

  • Framework: Spring Boot 3
  • Language: Java 21
  • Persistence: Spring Data JDBC
    • In-Memory: ConcurrentHashMap based implementation for fast testing.
    • Database: PostgreSQL for production/persistence.
  • Architecture: Hexagonal Architecture (Ports and Adapters), ensuring the domain logic remains independent of frameworks and databases.

UML Diagram of the backend

                                                              ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
                                                              │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).

Integration & CI/CD

The project utilizes a dual-track CI/CD strategy to balance speed and reliability:

  1. 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.
  2. 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.

Manual Testing

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:

Configuration

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 test profile.
    • 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.
  • Properties: Configuration is managed via application.properties and programmatic Config classes in src/main/java/com/todoapp/config/.

Development Methodology: BDD & TDD

This project was developed using Behavior-Driven Development (BDD) and Test-Driven Development (TDD), leveraging Cucumber as the foundation.

  • Three Semantic Interfaces:
    1. In-Memory: For rapid domain logic verification.
    2. Database: For persistence layer verification.
    3. 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.

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

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.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •