Capstone Project — Building a Complete Blog Platform

Project Requirements Review

Over the past 17 lectures, you built a blog platform piece by piece — each lecture adding one capability. This final lecture ties everything together into a coherent, production-ready system.

Functional Requirements

Our blog platform supports these user stories:

Visitors (unauthenticated):

  • Browse published posts with pagination and sorting
  • View a single post with its comments
  • Search posts by keyword
  • Browse posts by category or tag
  • View author profiles
  • Register a new account

Authenticated Users (ROLE_USER):

  • Everything visitors can do
  • Add comments on posts
  • Reply to existing comments
  • Upload and change their avatar
  • Edit their own profile

Authors (ROLE_AUTHOR):

  • Everything users can do
  • Create new blog posts (saved as DRAFT)
  • Edit and delete their own posts
  • Upload cover images for their posts
  • Publish their own draft posts
  • Manage tags on their own posts

Admins (ROLE_ADMIN):

  • Everything authors can do
  • Edit and delete any post
  • Publish any post
  • Manage categories (CRUD)
  • Manage tags (CRUD)
  • Change user roles
  • Deactivate user accounts
  • View cache statistics and system health

Non-Functional Requirements

  • Performance: Average response time < 200ms for cached endpoints, < 500ms for database queries
  • Security: JWT authentication, BCrypt password hashing, input validation, role-based authorization
  • Reliability: Health checks, graceful error handling, transaction management
  • Maintainability: Layered architecture, DTOs, consistent error format, comprehensive tests
  • Deployability: Docker containerization, environment-based configuration, CI/CD pipeline

System Architecture Diagram

The Full Stack

┌─────────────────────────────────────────────────────────────────┐
│                        CLIENTS                                  │
│   Browser    Mobile App    Postman    Other APIs                │
└──────────────────────────┬──────────────────────────────────────┘
                           │ HTTPS
┌──────────────────────────▼──────────────────────────────────────┐
│                      NGINX (Reverse Proxy)                      │
│   • SSL Termination                                             │
│   • Static file serving (/api/files/)                           │
│   • Rate limiting                                               │
│   • Gzip compression                                            │
└──────────────────────────┬──────────────────────────────────────┘
                           │ HTTP (port 8080)
┌──────────────────────────▼──────────────────────────────────────┐
│                 SPRING BOOT APPLICATION                          │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │                   Security Filter Chain                     │ │
│  │  CORS → JWT Filter → Authorization Filter                  │ │
│  └──────────────────────────┬─────────────────────────────────┘ │
│                              │                                   │
│  ┌──────────────────────────▼─────────────────────────────────┐ │
│  │                    Controller Layer                         │ │
│  │  AuthController  PostController  UserController             │ │
│  │  FileController  CategoryController  AdminController        │ │
│  │  • Receives HTTP requests                                   │ │
│  │  • Validates input (@Valid)                                  │ │
│  │  • Returns HTTP responses                                   │ │
│  └──────────────────────────┬─────────────────────────────────┘ │
│                              │                                   │
│  ┌──────────────────────────▼─────────────────────────────────┐ │
│  │                     Service Layer                           │ │
│  │  AuthService  PostService  UserService  CommentService      │ │
│  │  UploadService  CategoryService  TagService                 │ │
│  │  • Business logic & validation                              │ │
│  │  • Transaction management (@Transactional)                  │ │
│  │  • DTO ↔ Entity conversion                                  │ │
│  │  • Caching (@Cacheable, @CacheEvict)                        │ │
│  └──────────────────────────┬─────────────────────────────────┘ │
│                              │                                   │
│  ┌──────────────────────────▼─────────────────────────────────┐ │
│  │                   Repository Layer                          │ │
│  │  UserRepository  PostRepository  CommentRepository          │ │
│  │  CategoryRepository  TagRepository  UploadedFileRepository  │ │
│  │  • JpaRepository interfaces                                 │ │
│  │  • Derived queries, @Query (JPQL/native)                    │ │
│  │  • Pagination and sorting                                   │ │
│  └──────────────────────────┬─────────────────────────────────┘ │
│                              │                                   │
│  ┌──────────────────────────▼─────────────────────────────────┐ │
│  │             Hibernate / JPA / HikariCP                      │ │
│  │  • ORM mapping (Entity ↔ Table)                             │ │
│  │  • SQL generation                                           │ │
│  │  • Connection pooling                                       │ │
│  └──────────────────────────┬─────────────────────────────────┘ │
└──────────────────────────────┼──────────────────────────────────┘
                               │ JDBC (port 3306)
┌──────────────────────────────▼──────────────────────────────────┐
│                       MariaDB 11                                 │
│   • blogdb database                                              │
│   • Flyway-managed schema (V1–V13 migrations)                    │
│   • InnoDB engine, utf8mb4 charset                               │
│   • Indexes for query performance                                │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│                     FILESYSTEM                                    │
│   /var/data/blog-api/uploads/                                     │
│   ├── posts/      (cover images)                                  │
│   └── avatars/    (user avatars)                                  │
└──────────────────────────────────────────────────────────────────┘

Technology Stack Summary

Layer Technology Lecture
Language Java 17 Prerequisite
Framework Spring Boot 3.3 Lecture 1–2
Web Spring MVC Lecture 3
Database MariaDB 11 Lecture 4
ORM Spring Data JPA + Hibernate Lecture 5–7
Validation Jakarta Bean Validation Lecture 8
Error Handling @ControllerAdvice Lecture 8
Transactions Spring @Transactional Lecture 9
Logging SLF4J + Logback Lecture 9
Migration Flyway Lecture 10
Security Spring Security + JWT Lecture 11–12
File Storage Local filesystem Lecture 13
Documentation SpringDoc OpenAPI / Swagger Lecture 14
Testing JUnit 5, Mockito, TestContainers Lecture 15
Caching Caffeine Lecture 16
Deployment Docker, Docker Compose, Nginx Lecture 17
CI/CD GitHub Actions Lecture 17

Database Schema Final Design

Entity-Relationship Overview

users ──────────┐
  │              │
  │ 1:1          │ 1:N
  ▼              ▼
user_profiles   posts ◄──── categories (N:1)
                  │
                  ├─── comments (1:N, self-referencing for replies)
                  │
                  └─── post_tags ───► tags (M:N)
                  │
                  └─── uploaded_files (1:1 cover image)

users ──── uploaded_files (1:1 avatar)

Complete Table List

Table Purpose Key Relationships
users User accounts with credentials and roles Has one profile, has one avatar
user_profiles Extended user info (website, social links) Belongs to one user
categories Post grouping (Tutorial, News, etc.) Has many posts
tags Post labels (Java, Spring Boot, etc.) Many-to-many with posts
posts Blog articles Belongs to user and category, has many comments and tags
post_tags Junction table for posts ↔ tags References posts and tags
comments Post comments with nested replies Belongs to post and user, self-referencing parent
uploaded_files File metadata (name, path, type, size) Belongs to uploader (user)
flyway_schema_history Migration tracking (managed by Flyway) System table

Flyway Migrations Summary

V1__create_users_table.sql
V2__create_categories_table.sql
V3__create_tags_table.sql
V4__create_posts_table.sql
V5__create_post_tags_table.sql
V6__create_comments_table.sql
V7__add_views_count_to_posts.sql
V8__add_index_on_posts_published_at.sql
V9__normalize_post_status_values.sql
V10__remove_deprecated_meta_columns.sql
V11__ensure_user_security_fields.sql
V12__create_uploaded_files_table.sql
V13__add_performance_indexes.sql
V100__seed_initial_data.sql
R__create_post_statistics_view.sql

Each migration is immutable and version-controlled. Running all migrations from an empty database produces the complete schema.


Implementing All Features End-to-End

The Complete Project Package Structure

src/main/java/com/example/blogapi/
├── BlogApiApplication.java              ← Entry point
│
├── config/
│   ├── CacheConfig.java                 ← Caffeine cache configuration
│   └── OpenApiConfig.java               ← Swagger/OpenAPI configuration
│
├── controller/
│   ├── AuthController.java              ← Register, login, refresh token
│   ├── PostController.java              ← Post CRUD, publish, search, comments
│   ├── UserController.java              ← User profile, avatar upload
│   ├── CategoryController.java          ← Category CRUD
│   ├── TagController.java               ← Tag CRUD
│   ├── FileController.java              ← Serve uploaded files
│   └── AdminController.java             ← User management, cache stats
│
├── dto/
│   ├── CreatePostRequest.java           ← POST /api/posts body
│   ├── UpdatePostRequest.java           ← PUT /api/posts/{id} body
│   ├── PostResponse.java                ← Post data returned to client
│   ├── CreateCommentRequest.java        ← POST /api/posts/{id}/comments body
│   ├── CommentResponse.java             ← Comment data returned to client
│   ├── RegisterRequest.java             ← POST /api/auth/register body
│   ├── LoginRequest.java                ← POST /api/auth/login body
│   ├── AuthResponse.java                ← Login/register response with JWT
│   ├── UserResponse.java                ← User profile data
│   ├── FileUploadResponse.java          ← Upload result metadata
│   └── ErrorResponse.java               ← Consistent error format
│
├── exception/
│   ├── GlobalExceptionHandler.java      ← @ControllerAdvice for all errors
│   ├── ResourceNotFoundException.java   ← 404 errors
│   ├── DuplicateResourceException.java  ← 409 errors
│   └── BadRequestException.java         ← 400 errors
│
├── mapper/
│   ├── PostMapper.java                  ← Post entity ↔ PostResponse
│   ├── CommentMapper.java               ← Comment entity ↔ CommentResponse
│   └── UserMapper.java                  ← User entity ↔ UserResponse
│
├── model/
│   ├── BaseEntity.java                  ← @MappedSuperclass with audit fields
│   ├── User.java                        ← User entity
│   ├── UserProfile.java                 ← User profile entity
│   ├── Post.java                        ← Post entity with relationships
│   ├── Comment.java                     ← Comment entity (self-referencing)
│   ├── Category.java                    ← Category entity
│   ├── Tag.java                         ← Tag entity
│   └── UploadedFile.java                ← File metadata entity
│
├── repository/
│   ├── UserRepository.java              ← findByUsername, findByEmail, exists
│   ├── PostRepository.java              ← findByStatus, searchByKeyword, JOIN FETCH
│   ├── CommentRepository.java           ← findTopLevelByPostId, countByPostId
│   ├── CategoryRepository.java          ← findBySlug
│   ├── TagRepository.java               ← findBySlug
│   └── UploadedFileRepository.java      ← basic CRUD
│
├── security/
│   ├── SecurityConfig.java              ← Filter chain, CORS, endpoint rules
│   ├── JwtTokenProvider.java            ← Generate, validate, parse JWT
│   ├── JwtAuthenticationFilter.java     ← Extract token from every request
│   ├── JwtAuthenticationEntryPoint.java ← Custom 401 response
│   ├── CustomUserDetailsService.java    ← Load user from MariaDB
│   └── PostSecurity.java                ← isAuthor() check for @PreAuthorize
│
├── service/
│   ├── AuthService.java                 ← Register, login, refresh
│   ├── PostService.java                 ← Post business logic + caching
│   ├── CommentService.java              ← Comment business logic
│   ├── UserService.java                 ← User profile management
│   ├── CategoryService.java             ← Category business logic
│   ├── TagService.java                  ← Tag business logic
│   ├── FileStorageService.java          ← Store/load/delete files on disk
│   └── UploadService.java               ← Tie files to posts and users
│
└── validation/
    ├── UniqueUsername.java               ← Custom validator annotation
    └── UniqueUsernameValidator.java      ← Validator implementation

This is approximately 45 Java files — a realistic size for a well-structured Spring Boot application.


User Registration, Login, JWT Auth

The Complete Auth Flow

Register:  POST /api/auth/register
             → Validate input (@Valid)
             → Check duplicate username/email
             → Hash password (BCrypt)
             → Save user (ROLE_USER)
             → Return success message

Login:     POST /api/auth/login
             → Validate input (@Valid)
             → AuthenticationManager.authenticate()
               → CustomUserDetailsService.loadUserByUsername()
               → PasswordEncoder.matches()
             → Generate access token (24h)
             → Generate refresh token (7d)
             → Return tokens + user info

Refresh:   POST /api/auth/refresh
             → Validate refresh token
             → Extract username from token
             → Verify user is still active
             → Generate new access token
             → Return new token

Every request:
             → JwtAuthenticationFilter extracts token from Authorization header
             → JwtTokenProvider validates signature + expiration
             → SecurityContext populated with user info
             → @PreAuthorize checks permissions
             → Controller handles request

Key Security Decisions

Decision Choice Why
Password storage BCrypt (strength 10) Industry standard, salted, intentionally slow
Token type JWT (HS256) Stateless, self-contained, cross-platform
Access token TTL 24 hours Balance between security and UX
Refresh token TTL 7 days Users stay logged in for a week
Session management STATELESS No server-side session state
CSRF Disabled Not needed for stateless JWT APIs

CRUD for Posts, Comments, Tags, Categories

Complete API Endpoint Map

Posts:

GET    /api/posts                    ← List posts (paginated, filterable)
GET    /api/posts/{id}               ← Get single post with details
GET    /api/posts/slug/{slug}        ← Get post by URL slug
GET    /api/posts/search?keyword=    ← Search posts
POST   /api/posts                    ← Create post [AUTHOR, ADMIN]
PUT    /api/posts/{id}               ← Update post [author of post, ADMIN]
PATCH  /api/posts/{id}/publish       ← Publish post [author of post, ADMIN]
PATCH  /api/posts/{id}/archive       ← Archive post [ADMIN]
DELETE /api/posts/{id}               ← Delete post [ADMIN]
POST   /api/posts/{id}/cover-image   ← Upload cover image [author of post, ADMIN]
POST   /api/posts/{id}/comments      ← Add comment [authenticated]
GET    /api/posts/{id}/comments      ← List comments (paginated)

Categories:

GET    /api/categories               ← List all categories
GET    /api/categories/{slug}        ← Get category by slug
POST   /api/categories               ← Create category [ADMIN]
PUT    /api/categories/{id}          ← Update category [ADMIN]
DELETE /api/categories/{id}          ← Delete category [ADMIN]

Tags:

GET    /api/tags                     ← List all tags
POST   /api/tags                     ← Create tag [ADMIN]
DELETE /api/tags/{id}                ← Delete tag [ADMIN]

Users:

GET    /api/users/{id}               ← Get user profile (public info)
PUT    /api/users/me                 ← Update own profile [authenticated]
POST   /api/users/me/avatar          ← Upload avatar [authenticated]

Admin:

GET    /api/admin/users              ← List all users [ADMIN]
PATCH  /api/admin/users/{id}/role    ← Change user role [ADMIN]
PATCH  /api/admin/users/{id}/deactivate ← Deactivate user [ADMIN]
GET    /api/admin/cache/stats        ← Cache statistics [ADMIN]
DELETE /api/admin/cache              ← Clear all caches [ADMIN]

Auth:

POST   /api/auth/register            ← Register new user
POST   /api/auth/login               ← Login and get JWT tokens
POST   /api/auth/refresh             ← Refresh access token

Files:

GET    /api/files/{*path}            ← Serve uploaded file (public)

Total: ~30 endpoints covering all CRUD operations, authentication, file management, and administration.


Search, Pagination, Filtering

Standard Pagination Parameters

Every list endpoint follows the same pagination pattern:

GET /api/posts?page=0&size=10&sort=createdAt&direction=desc&status=PUBLISHED
Parameter Default Description
page 0 Page number (0-indexed)
size 10 Items per page
sort createdAt Field to sort by
direction desc Sort direction (asc/desc)

Standard Pagination Response

Every paginated response uses Spring Data’s Page:

{
  "content": [ ... ],
  "pageable": {
    "pageNumber": 0,
    "pageSize": 10,
    "sort": { "sorted": true }
  },
  "totalElements": 45,
  "totalPages": 5,
  "first": true,
  "last": false,
  "numberOfElements": 10
}

Frontend developers can build pagination controls using totalPages, first, last, and pageNumber.

Search Implementation

GET /api/posts/search?keyword=spring+boot&page=0&size=10

The search query uses JPQL with LIKE for simple search or MariaDB FULLTEXT index for advanced search:

// JPQL search (works without special indexes)
@Query("SELECT p FROM Post p WHERE " +
       "LOWER(p.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " +
       "LOWER(p.content) LIKE LOWER(CONCAT('%', :keyword, '%'))")
Page<Post> searchByKeyword(@Param("keyword") String keyword, Pageable pageable);

Filtering Options

GET /api/posts?status=PUBLISHED              ← Filter by status
GET /api/posts?tag=spring-boot               ← Filter by tag slug
GET /api/posts?category=tutorial             ← Filter by category slug
GET /api/posts?author=alice                  ← Filter by author username

File Upload for Post Images

Upload Flow

Client                                    Server
  |                                          |
  | POST /api/posts/1/cover-image            |
  | Authorization: Bearer <token>            |
  | Content-Type: multipart/form-data        |
  | [file binary data]                       |
  | ─────────────────────────────────────────>|
  |                                          |
  |    1. Validate file (type, size, name)   |
  |    2. Generate unique filename (UUID)    |
  |    3. Store file on disk                 |
  |    4. Save metadata to uploaded_files    |
  |    5. Link file to post (cover_image_id) |
  |    6. Delete old cover image if exists   |
  |                                          |
  | 201 Created                              |
  | {"fileId":1,"url":"/api/files/..."}      |
  | <─────────────────────────────────────────|

File Serving

Uploaded files are served publicly with caching headers:

GET /api/files/posts/a1b2c3d4.jpg
  → FileStorageService.loadFileAsResource()
  → Response with Content-Type: image/jpeg
  → Cache-Control: max-age=86400 (browser caches for 24h)

In production with Nginx, static files are served directly by Nginx (bypassing Spring Boot) for better performance.


Role-Based Access (Admin, Author, Reader)

The Authorization Matrix

Action Visitor USER AUTHOR ADMIN
Browse published posts
Search posts
Register account
Add comments
Upload avatar
Create posts
Edit own posts
Delete own posts
Edit any post
Delete any post
Manage categories
Manage users
View system health

Implementation Layers

Authorization is enforced at two levels:

Level 1: URL-based (SecurityConfig) — Broad rules based on HTTP method and path.

.requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/posts").hasAnyRole("AUTHOR", "ADMIN")
.requestMatchers("/api/admin/**").hasRole("ADMIN")

Level 2: Method-based (@PreAuthorize) — Fine-grained rules with business logic.

@PreAuthorize("hasRole('ADMIN') or @postSecurity.isAuthor(#id, authentication.name)")
public PostResponse updatePost(Long id, UpdatePostRequest request) { ... }

API Documentation, Testing, Docker Deployment

API Documentation

Swagger UI at /swagger-ui.html provides:

  • Every endpoint with parameters and request/response schemas
  • “Try it out” functionality for interactive testing
  • JWT authentication via the “Authorize” button
  • Grouped by tags: Authentication, Posts, Users, Categories, Tags, Files, Admin

Testing Strategy

Test Type Count Coverage
Service layer unit tests (Mockito) ~30 Business logic, validation, exceptions
Repository tests (@DataJpaTest) ~15 Custom queries, pagination, data integrity
Controller tests (@WebMvcTest) ~20 HTTP status codes, validation, auth
Integration tests (@SpringBootTest) ~5 Full flows: register → login → CRUD
Total ~70 Service: 80%+, Controller: 70%+

Run all tests:

./mvnw verify

Docker Deployment

# Development
docker compose up -d

# Production (with .env secrets)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

The application starts in under 15 seconds:

MariaDB: Ready for connections       (5s)
Flyway:  Applied 15 migrations       (3s)
Hibernate: Schema validated          (2s)
Tomcat:  Started on port 8080        (3s)
Application ready                    (Total: ~13s)

Code Review Checklist & Best Practices Recap

Architecture Checklist

☐ Layered architecture: Controller → Service → Repository
☐ Dependencies flow downward only
☐ Controllers handle HTTP, not business logic
☐ Services handle business logic, not HTTP
☐ Repositories handle data access, not business logic
☐ DTOs separate API contract from domain model
☐ Entities never cross the controller boundary
☐ Mappers convert between entities and DTOs

Entity Design Checklist

☐ Every relationship uses FetchType.LAZY
☐ @ManyToMany uses Set, not List
☐ Collections are initialized (= new ArrayList<>())
☐ equals() and hashCode() use id only, with null safety
☐ Helper methods keep bidirectional relationships in sync
☐ No @OneToMany for potentially unbounded collections
☐ @JsonIgnore on entity back-references (or use DTOs exclusively)
☐ @PrePersist/@PreUpdate or BaseEntity for timestamps

Service Layer Checklist

☐ @Transactional(readOnly = true) at class level
☐ @Transactional on write methods
☐ Guard clauses validate before doing work
☐ Custom exceptions (ResourceNotFoundException, BadRequestException)
☐ Logging at INFO for business events, WARN for unusual cases
☐ Private helper methods for reusable logic
☐ DTO mapping while Hibernate session is open

Security Checklist

☐ Passwords hashed with BCrypt (never stored plain text)
☐ JWT secret is 256+ bits, stored as environment variable
☐ CSRF disabled (stateless API)
☐ Sessions disabled (STATELESS)
☐ Public endpoints explicitly marked with permitAll()
☐ @PreAuthorize for method-level authorization
☐ Custom AuthenticationEntryPoint for consistent 401 responses
☐ File uploads validated (type, size, filename)

Database Checklist

☐ Flyway manages all schema changes (ddl-auto=validate)
☐ Migrations are immutable once applied
☐ Indexes on columns used in WHERE, ORDER BY, JOIN
☐ Foreign keys enforce referential integrity
☐ utf8mb4 charset for emoji support
☐ No SELECT * — use projections for list pages
☐ N+1 problems fixed with JOIN FETCH or @EntityGraph

Testing Checklist

☐ Unit tests for service layer business logic
☐ @DataJpaTest for custom repository queries
☐ @WebMvcTest for controller HTTP behavior
☐ Integration tests for critical flows (auth, CRUD)
☐ Tests follow Arrange-Act-Assert pattern
☐ Each test is independent (no shared mutable state)
☐ 80%+ line coverage on service layer

Deployment Checklist

☐ Multi-stage Dockerfile (build + runtime)
☐ Non-root user in Docker container
☐ Docker health check configured
☐ docker-compose.yml with depends_on health checks
☐ Named volumes for data persistence
☐ Secrets in environment variables, not in code
☐ Actuator health endpoint exposed
☐ Swagger UI disabled in production
☐ Logging to file with rotation

What You Have Learned

Looking back at where you started — a Java developer with no web experience — here is what you now know:

Web Fundamentals: HTTP protocol, request/response cycle, REST architecture, client-server communication, content negotiation.

Spring Boot: Auto-configuration, dependency injection, component scanning, profiles, property management, embedded server.

REST API Design: HTTP methods mapped to CRUD, path variables vs query parameters, response entities, status codes, API versioning.

Database: MariaDB administration, SQL queries, indexing, normalization, entity-relationship design.

ORM: JPA entities, Hibernate mapping, relationships (1:1, 1:N, M:N), cascade types, fetch strategies, dirty checking.

Spring Data JPA: Repository interfaces, derived queries, JPQL, native SQL, pagination, projections, auditing.

Architecture: Layered architecture, DTO pattern, service layer design, transaction management, business logic patterns.

Data Integrity: Bean validation, custom validators, global error handling, consistent error responses.

Logging: SLF4J, log levels, parameterized messages, file output, environment-specific configuration.

Database Migration: Flyway versioned migrations, repeatable migrations, baseline, team workflow.

Security: Spring Security filter chain, authentication vs authorization, BCrypt, JWT tokens, role-based access control.

Performance: Caching with Caffeine, query optimization with EXPLAIN, indexing strategies, connection pooling, batch operations.

Testing: Unit tests (Mockito), repository tests (@DataJpaTest), controller tests (@WebMvcTest), integration tests (@SpringBootTest), TestContainers.

Deployment: Docker, Docker Compose, Nginx reverse proxy, CI/CD with GitHub Actions, production configuration, health checks.

API Documentation: OpenAPI specification, Swagger UI, schema annotations, interactive testing.


Where to Go Next

This series gave you a complete foundation. Here are paths to continue your growth:

Deepen Spring Boot

  • Spring WebFlux — Reactive programming for high-concurrency applications
  • Spring Cloud — Microservices patterns (service discovery, config server, circuit breakers)
  • Spring Batch — Large-scale batch processing (data imports, report generation)
  • Spring WebSocket — Real-time bidirectional communication

Expand the Blog Platform

  • Add a React or Vue frontend that consumes your API
  • Implement email notifications (new comment, post published)
  • Add full-text search with Elasticsearch
  • Implement real-time comments with WebSocket
  • Add social login (OAuth2 with Google/GitHub)
  • Build an admin dashboard with analytics

Production Skills

  • Kubernetes — Container orchestration for scaling beyond single-server
  • Monitoring — Prometheus + Grafana for metrics visualization
  • Log aggregation — ELK Stack (Elasticsearch, Logstash, Kibana)
  • Message queues — RabbitMQ or Kafka for async processing
  • CDN — CloudFront or Cloudflare for static file delivery

Best Practices

  • Read “Effective Java” by Joshua Bloch — the definitive guide to writing good Java
  • Study “Clean Architecture” by Robert Martin — architecture principles that transcend frameworks
  • Practice on real projects — build something you actually use

Final Words

You started this series knowing Java but nothing about web development. Now you have built a production-ready REST API with authentication, file uploads, caching, testing, and Docker deployment — from scratch, understanding every layer.

The most important skill you have developed is not Spring Boot or JPA or Docker. It is the ability to decompose a complex system into manageable layers, each with clear responsibilities and well-defined interfaces. This skill transfers to any framework, any language, any platform.

Build something real. Deploy it. Show it to people. Break it, fix it, improve it. That is how you grow from a student to an engineer.

Good luck.

Leave a Reply

Your email address will not be published. Required fields are marked *