Backend Engineering

Production Grade Task Management API

A backend engineering learning project that brings together REST API design, authentication, project and task workflows, persistence, caching, load balancing, containerisation and observability.

AdvancedJava 21Spring BootSpring SecuritySpring Data JPAPostgreSQLRedisNGINXDocker ComposeOpenTelemetryMicrometerJWTMaven
View source repository ↗

Project overview

This project explores how a production-oriented task-management backend can be designed beyond basic CRUD endpoints. The domain includes users, projects, project membership, tasks and comments. The architecture material describes a modular applicaiton with clear controller, service and repository responsibilities; PostgreSQL as the source of truth; Redis as a performance layer; and asynchronous processing for work such as notificatios. The public repository contains a Java 21/ Spring Boot application, Docker and NGINX configuration, multiple API containers, PostgreSQL, Redis and OpenTelemetry Collector / Jaeger observability path.

Capabilities and scope

✓User registration and login with JWT-based credentials.
✓User profile retrieval and updates.
✓Project creation, retrieval, update and deletion
✓Project membership management and role-aware operations
✓Task creation, listing, updates, assignment, status and priority changes
✓Task comments and comment moderation operations
✓Resource-level authorisation enforced by the backend
✓Pagination and filtered task retrieval
✓Redis-backed caching and cache-key management
✓Rate limiting support and request / trace context components
✓Containerised multi-instance deployment build NGINX
✓Health endpoints, metrics and OpenTelemetry-compatible tracing configuration

Architecture at a glance

The following sequence summarises the principal components and boundaries described in the project material.

  1. 1Client sends a REST request to the single NGINX entry point
  2. 2NGINX distributes traffic across interchangeable Spring Boot API instances
  3. 3Spring Security authenticates the request and the application enforces resource-level permissions
  4. 4Controllers validate request DTOs and delegate application work to services
  5. 5Services apply business rules and transactional boundaries
  6. 6Repositories persist authoritative data in PostgreSQL
  7. 7Redis stores selected frequently accessed data as a performance layer
  8. 8OpenTelemetry Collector receives telemetry and Jaeger provides trace visualisation in the local Compose topology

1. Project motivation: move beyond CRUD

A CRUD-only task manager demonstrates endpoint wiring, but it does not fully exerise the concerns that appear in backend systems with multiple users and related resources. This project is structured around those deeper concerns: a user must only access projects they belong to, a projectt creator becomes its owenr, a task belongs to a project, assignments must respect membership, and related daabases writes must remain consistent. The goal is to understand how requirments, domain boundaries, security, persistence and operations fit together.

2. Domain model and resource relationships

The supplied architecture notes identify User, Project, ProjectMemebr, Task and Comment as the main domain records. A user can own multiple projects. A project contains multiple tasks. Project memebrship is many-to-many and therefore needs a junction record that can carry a role. A task can have multiple comments. These relationships shape both the relational schema and authorisation checks: access to a task is normally evaluated in the context of the project that owns it.

Domain relaionshi sketchtext
User 1 -------- N Project
Project 1 ----- N Task
Project N ----- N User (ProjectMember)
Task 1 -------- N Comment

3. Functional requirements define the API surface

The requirements document separates the system into idenity, profile, project, membership, task and comment workflows. Registration accepts a name, email and password; login provides credentials for protected API requests. Project creation records an owner. Task operations include filtering by status, priority and assignee, and large collections must be paginated. Comment ownership and project membership are important authorisation boundaries. This creates a much richer set of test cases than a collection of unrestricted create/read/update/delete handlers.

  • Identify: register, authenticate and manage the current user's profile.
  • Projects: create, list, retrieve, update, delete and manage members.
  • Tasks: create, list with filters, update, delete, assign, prioritise and change status.
  • Comments: create, list, update one's own comment and delete where authorised.
  • Security: enforce ownership, membership and moderation rules on the backend.

4. Layered application responsibilities

The codebase is organised around packages for controllers, DTOs, configuratio,, cache, security, repsositories and other application responsibilities. A controller receievs HTTP input and maps the request to an application operation. Request DTOs define the accepted external contract instead of exposing persistence entities directly. The service layer coordinates business rules, authorisation and transactions. Repositories handle data access. Keeping these responsibilities explicit helps isolate HTTP concerns from domain behavior and persistence details.

5. Authentication is not the same as authorisation

JWT-based authentication establishes the identity associated with a request. Resource-level authorisation then decides whether that identity may operate on a particular project, task or comment. A valid tokien alone must not grant access to every resource. The requirement explicitly state that a user cannot access projects they are not a memebr of and cannot modify another user's profile or coment unless authorised. These decisions belong in trusted server-side code, not only in frontend navigation or button visibility.

6. PostgreSQL is the source of truth; Redis is the performance layer

The architecture material assigns persistent user, project, membership, task and comment data to PostgreSQL. Redis is intended for selected frequently accessed data such as user profiles, project metadata, membership information and task lists. The repository includes Redis configuration and cache service classes for projects and tasks, plus cache-key construction. Cache invalidation must follow successful database writes so a cached TODO task does not continue to appear after the authoritative record changes to COMPLETED. Critical state should not exist only in Redis.

7. Transactions, pagination and concurrency

Some operations must update multiple records atomically. Creating a project and establishing the creator's owner membership is one example: both should commit or roll back together. The requirements also call for paginated project, member, task and comment collections to avoid unbounded responses. For large result sets, keyset pagination may be preferable to large offsets. Concurrent updates should be considered explicitly; database transactions and appropriate optimistic-locking strategies can help prevent lost updates where the domain requires them.

8. Scale-out topology and request routing

The repository's Docker Compose configuration defines an NGINX container and three API containers, each running the same application image. PostgreSQL and Redis are shared dependencies. This illustrates a scale-out application tier: any API instance can receive a request, provided important session or application state is not tied to local process memory. The NGINX configuration is mounted into the proxy container, while Compose network configuration allows services to communicate by service name.

Repository Compose topologytext
Client
  |
NGINX :8080
  |---- API 1 :8080
  |---- API 2 :8080
  |---- API 3 :8080
          |
          +---- PostgreSQL 17
          +---- Redis 7
          +---- OpenTelemetry Collector
                    |
                    +---- Jaeger UI

9. Observability and operational diagnosis

The repository includes Actuator, Micrometer Prometheus support, OpenTelemetry-related dependencies, trace ID and request-logging filters, an OpenTelemetry Collector configuration and a Jaeger service in the Compose topology. These pieces help make an HTTP request observable as it moves through application components and dependencies. Metrics can reveal request rate, latency and error rate; logs provide diagnostic context; traces help identify where a distributed request spends time. Secrets and authentication tokens must never be written to logs.

10. Quality targets and architectural trade-offs

The supplied requirements set planning targets of 99.9% monthly availability, p50 latency below 100 ms, p95 below 200 ms, p99 below 500 ms and approximately 500 requests per second at peak. The architecture exercise also contains an earlier 1,200 RPS assumption, so the current approved workload baseline should be clarified before treating either number as a verified capacity. The project notes favour a modular application initially because it is simpler to develop, deploy and debug than microservices. Caching improves repeated reads but introduces freshness concerns; asynchronous notifications improve request responsiveness but introduce eventual consistency and worker operations.

What this project teaches

  • ✓Design REST APIs around explicit functional and quality requirements.
  • ✓Separate request DTOs, cotroller, services, repositories and persistence entities.
  • ✓Distinguish token authentication from project/task/comment authorisation.
  • ✓Use database transactions to protect related writes.
  • ✓Reason about cache-aside, cache invalidation and source-of-truth ownership.
  • ✓Run multiple application instance behind a reverse proxy using Docker Compose.
  • ✓Use logs, metrics and traces to reason about operational behavior.

Project links