Building a Movie Ticketing System with Spring Cloud Microservices

System Architecture Overview

This movie ticketing system employs a microservices architecture with the following components:

Core Services:

  • Movie Service: Handles movie data operations including creation, retrieval, and search
  • User Service: Manages ticket purchasing and movie queries
  • Eureka Server: Service registry for service discovery
  • Config Server: Centralized configuration management from Git repository
  • Zuul Gateway: API gateway with request filtering and routing

Additional Features:

  • Feign client for service communication with load balancign
  • Hystrix for circuit breaking and fault tolerance
  • RabbitMQ for configuration refresh notifications

Project Structure

The multi-module Maven project consists of:

  • common: Shared entities and utilities
  • config-server: Configuration management service
  • eureka-movie: Movie data provider service
  • eureka-user: Ticket purchasing service
  • eureka-server: Service registry
  • zuul-gateway: API gateway

Core Implementation

Shared Entity Definition

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Film {
    private Integer filmId;
    private String title;
    private Integer ticketPrice;
    private String screeningStart;
    private String screeningEnd;
}

Configuration Server Setup

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableConfigServer
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

Configuration server properties:

server:
  port: 40010
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/your-repo/spring-cloud-config.git

Movie Service Implementation

Data access layer:

@Mapper
public interface FilmRepository {
    @Insert("INSERT INTO films(title, ticket_price, screening_start, screening_end) VALUES (#{title}, #{ticketPrice}, #{screeningStart}, #{screeningEnd})")
    void saveFilm(Film film);
    
    @Select("SELECT * FROM films")
    List<Film> findAllFilms();
    
    @Select("SELECT * FROM films WHERE title = #{title}")
    List<Film> findFilmsByTitle(@Param("title") String title);
}

Service controller:

@RestController
@RequestMapping("/films")
public class FilmController {
    @Autowired
    FilmRepository filmRepository;
    
    @PostMapping
    public void addFilm(@RequestBody Film film) {
        filmRepository.saveFilm(film);
    }
    
    @GetMapping
    public List<Film> getAllFilms() {
        return filmRepository.findAllFilms();
    }
    
    @GetMapping("/search/{title}")
    public List<Film> searchFilms(@PathVariable String title) {
        return filmRepository.findFilmsByTitle(title);
    }
}

User Service Implementation

Service client with Feign:

@FeignClient(value = "MOVIE-SERVICE", fallbackFactory = FilmServiceFallback.class)
public interface FilmServiceClient {
    @GetMapping("/films")
    List<Film> getAllFilms();
    
    @GetMapping("/films/search/{title}")
    List<Film> getFilmsByTitle(@PathVariable String title);
}

Circuit breaker fallback implementation:

@Component
public class FilmServiceFallback implements FallbackFactory<FilmServiceClient> {
    @Override
    public FilmServiceClient create(Throwable cause) {
        return new FilmServiceClient() {
            @Override
            public List<Film> getAllFilms() {
                cause.printStackTrace();
                return Arrays.asList(new Film(503, "Service Unavailable", 0, "N/A", "N/A"));
            }
            
            @Override
            public List<Film> getFilmsByTitle(String title) {
                return Arrays.asList(new Film(503, "Service Unavailable", 0, "N/A", "N/A"));
            }
        };
    }
}

Ticket purchasing controller:

@RestController
@RequestMapping("/tickets")
public class TicketController {
    @Autowired
    private FilmServiceClient filmServiceClient;
    @Autowired
    private TicketRepository ticketRepository;
    
    @GetMapping
    public List<Film> browseFilms() {
        return filmServiceClient.getAllFilms();
    }
    
    @PostMapping("/purchase/{filmTitle}")
    public List<Film> purchaseTicket(@PathVariable String filmTitle) {
        List<Film> availableFilms = filmServiceClient.getFilmsByTitle(filmTitle);
        
        if (availableFilms.isEmpty()) {
            return Arrays.asList(new Film(404, "Film not found", 0, "N/A", "N/A"));
        }
        
        Film selectedFilm = availableFilms.get(0);
        ticketRepository.savePurchase(new Film(null, selectedFilm.getTitle(), 
            selectedFilm.getTicketPrice(), selectedFilm.getScreeningStart(), 
            selectedFilm.getScreeningEnd()));
        
        return availableFilms;
    }
}

Service Discovery Configuration

Eureka server setup:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaServer
public class DiscoveryServer {
    public static void main(String[] args) {
        SpringApplication.run(DiscoveryServer.class, args);
    }
}

Client service registration:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:10077/eureka
  instance:
    prefer-ip-address: true

API Gateway Configuration

Zuul gateway setup:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

Gateway routing configuration:

zuul:
  routes:
    user.serviceId: user-service
    user.path: /tickets/**
    film.serviceId: movie-service
    film.path: /films/**
  ignored-services: "*"
  prefix: "/api"

Load Balancing Configuration

@Configuration
public class LoadBalancerConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public IRule loadBalancingRule() {
        return new RandomRule();
    }
}

Service Configuration Management

Client services bootstrap configuration:

spring:
  cloud:
    config:
      uri: http://localhost:40010
      label: master
      name: application-service
      profile: dev

Service-specific configurations stored in Git:

User Service Configuration:

server:
  port: 10088
spring:
  application:
    name: user-service
feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000

Movie Service Configuration:

server:
  port: 10099
spring:
  application:
    name: movie-service

Tags: Spring Cloud microservices java Eureka Zuul

Posted on Wed, 13 May 2026 02:16:11 +0000 by kaimason1